diff options
| author | Cy Schubert <cy@FreeBSD.org> | 2018-12-06 05:04:28 +0000 |
|---|---|---|
| committer | Cy Schubert <cy@FreeBSD.org> | 2018-12-06 05:04:28 +0000 |
| commit | 8a36c5c2ca4d1f8a900ca3d9ffde40b96463def7 (patch) | |
| tree | b9a3166587c75d5325dc46c7c83ca435f2e54917 /src | |
| parent | 765ef8a7642d07aa9616f2b1a9cdebb8e3552f6a (diff) | |
Diffstat (limited to 'src')
291 files changed, 52172 insertions, 7549 deletions
diff --git a/src/ap/Makefile b/src/ap/Makefile index 98788fef797e..9b07ee163419 100644 --- a/src/ap/Makefile +++ b/src/ap/Makefile @@ -10,12 +10,15 @@ include ../lib.rules CFLAGS += -DHOSTAPD CFLAGS += -DNEED_AP_MLME +CFLAGS += -DCONFIG_ETH_P_OUI CFLAGS += -DCONFIG_HS20 CFLAGS += -DCONFIG_INTERWORKING CFLAGS += -DCONFIG_IEEE80211R +CFLAGS += -DCONFIG_IEEE80211R_AP CFLAGS += -DCONFIG_IEEE80211W CFLAGS += -DCONFIG_WPS CFLAGS += -DCONFIG_PROXYARP +CFLAGS += -DCONFIG_IPV6 CFLAGS += -DCONFIG_IAPP LIB_OBJS= \ @@ -32,6 +35,7 @@ LIB_OBJS= \ dhcp_snoop.o \ drv_callbacks.o \ eap_user_db.o \ + eth_p_oui.o \ gas_serv.o \ hostapd.o \ hs20.o \ @@ -43,14 +47,17 @@ LIB_OBJS= \ ieee802_11_shared.o \ ieee802_11_vht.o \ ieee802_1x.o \ + neighbor_db.o \ ndisc_snoop.o \ p2p_hostapd.o \ - peerkey_auth.o \ pmksa_cache_auth.o \ preauth_auth.o \ + rrm.o \ sta_info.o \ tkip_countermeasures.o \ utils.o \ + vlan.o \ + vlan_ifconfig.o \ vlan_init.o \ wmm.o \ wnm_ap.o \ diff --git a/src/ap/acs.c b/src/ap/acs.c index 5e8380535854..6d4c0416dd42 100644 --- a/src/ap/acs.c +++ b/src/ap/acs.c @@ -260,7 +260,7 @@ static void acs_clean_chan_surveys(struct hostapd_channel_data *chan) } -static void acs_cleanup(struct hostapd_iface *iface) +void acs_cleanup(struct hostapd_iface *iface) { int i; struct hostapd_channel_data *chan; @@ -314,7 +314,7 @@ acs_survey_interference_factor(struct freq_survey *survey, s8 min_nf) /* TODO: figure out the best multiplier for noise floor base */ factor = pow(10, survey->nf / 5.0L) + - (busy / total) * + (total ? (busy / total) : 0) * pow(2, pow(10, (long double) survey->nf / 10.0L) - pow(10, (long double) min_nf / 10.0L)); @@ -331,10 +331,8 @@ acs_survey_chan_interference_factor(struct hostapd_iface *iface, long double int_factor = 0; unsigned count = 0; - if (dl_list_empty(&chan->survey_list)) - return; - - if (chan->flag & HOSTAPD_CHAN_DISABLED) + if (dl_list_empty(&chan->survey_list) || + (chan->flag & HOSTAPD_CHAN_DISABLED)) return; chan->interference_factor = 0; @@ -359,9 +357,8 @@ acs_survey_chan_interference_factor(struct hostapd_iface *iface, (unsigned long) survey->channel_time_rx); } - if (!count) - return; - chan->interference_factor /= count; + if (count) + chan->interference_factor /= count; } @@ -450,13 +447,9 @@ static int acs_surveys_are_sufficient(struct hostapd_iface *iface) for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; - if (chan->flag & HOSTAPD_CHAN_DISABLED) - continue; - - if (!acs_survey_list_is_sufficient(chan)) - continue; - - valid++; + if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && + acs_survey_list_is_sufficient(chan)) + valid++; } /* We need at least survey data for one channel */ @@ -466,13 +459,9 @@ static int acs_surveys_are_sufficient(struct hostapd_iface *iface) static int acs_usable_chan(struct hostapd_channel_data *chan) { - if (dl_list_empty(&chan->survey_list)) - return 0; - if (chan->flag & HOSTAPD_CHAN_DISABLED) - return 0; - if (!acs_survey_list_is_sufficient(chan)) - return 0; - return 1; + return !dl_list_empty(&chan->survey_list) && + !(chan->flag & HOSTAPD_CHAN_DISABLED) && + acs_survey_list_is_sufficient(chan); } @@ -788,10 +777,7 @@ static int acs_study_survey_based(struct hostapd_iface *iface) static int acs_study_options(struct hostapd_iface *iface) { - int err; - - err = acs_study_survey_based(iface); - if (err == 0) + if (acs_study_survey_based(iface) == 0) return 0; /* TODO: If no surveys are available/sufficient this is a good @@ -920,14 +906,11 @@ static int acs_request_scan(struct hostapd_iface *iface) enum hostapd_chan_status acs_init(struct hostapd_iface *iface) { - int err; - wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit"); if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) { wpa_printf(MSG_INFO, "ACS: Offloading to driver"); - err = hostapd_drv_do_acs(iface->bss[0]); - if (err) + if (hostapd_drv_do_acs(iface->bss[0])) return HOSTAPD_CHAN_INVALID; return HOSTAPD_CHAN_ACS; } @@ -937,8 +920,7 @@ enum hostapd_chan_status acs_init(struct hostapd_iface *iface) acs_cleanup(iface); - err = acs_request_scan(iface); - if (err < 0) + if (acs_request_scan(iface) < 0) return HOSTAPD_CHAN_INVALID; hostapd_set_state(iface, HAPD_IFACE_ACS); diff --git a/src/ap/acs.h b/src/ap/acs.h index fc85259e85d5..ec84f0ee97f3 100644 --- a/src/ap/acs.h +++ b/src/ap/acs.h @@ -13,6 +13,7 @@ #ifdef CONFIG_ACS enum hostapd_chan_status acs_init(struct hostapd_iface *iface); +void acs_cleanup(struct hostapd_iface *iface); #else /* CONFIG_ACS */ @@ -22,6 +23,10 @@ static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface) return HOSTAPD_CHAN_INVALID; } +static inline void acs_cleanup(struct hostapd_iface *iface) +{ +} + #endif /* CONFIG_ACS */ #endif /* ACS_H */ diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 228de2baf946..f9b6f295927f 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -10,9 +10,11 @@ #include "utils/common.h" #include "crypto/sha1.h" +#include "crypto/tls.h" #include "radius/radius_client.h" #include "common/ieee802_11_defs.h" #include "common/eapol_common.h" +#include "common/dhcp.h" #include "eap_common/eap_wsc_common.h" #include "eap_server/eap.h" #include "wpa_auth.h" @@ -36,6 +38,10 @@ static void hostapd_config_free_vlan(struct hostapd_bss_config *bss) } +#ifndef DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES +#define DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES 0 +#endif /* DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES */ + void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) { dl_list_init(&bss->anqp_elem); @@ -55,6 +61,10 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) bss->wpa_group_rekey = 600; bss->wpa_gmk_rekey = 86400; + bss->wpa_group_update_count = 4; + bss->wpa_pairwise_update_count = 4; + bss->wpa_disable_eapol_key_retries = + DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES; bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; bss->wpa_pairwise = WPA_CIPHER_TKIP; bss->wpa_group = WPA_CIPHER_TKIP; @@ -88,13 +98,39 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) /* Set to -1 as defaults depends on HT in setup */ bss->wmm_enabled = -1; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP bss->ft_over_ds = 1; -#endif /* CONFIG_IEEE80211R */ + bss->rkh_pos_timeout = 86400; + bss->rkh_neg_timeout = 60; + bss->rkh_pull_timeout = 1000; + bss->rkh_pull_retries = 4; + bss->r0_key_lifetime = 1209600; +#endif /* CONFIG_IEEE80211R_AP */ bss->radius_das_time_window = 300; bss->sae_anti_clogging_threshold = 5; + bss->sae_sync = 5; + + bss->gas_frag_limit = 1400; + +#ifdef CONFIG_FILS + dl_list_init(&bss->fils_realms); + bss->fils_hlp_wait_time = 30; + bss->dhcp_server_port = DHCP_SERVER_PORT; + bss->dhcp_relay_port = DHCP_SERVER_PORT; +#endif /* CONFIG_FILS */ + + bss->broadcast_deauth = 1; + +#ifdef CONFIG_MBO + bss->mbo_cell_data_conn_pref = -1; +#endif /* CONFIG_MBO */ + + /* Disable TLS v1.3 by default for now to avoid interoperability issue. + * This can be enabled by default once the implementation has been fully + * completed and tested with other implementations. */ + bss->tls_flags = TLS_CONN_DISABLE_TLSv1_3; } @@ -192,6 +228,11 @@ struct hostapd_config * hostapd_config_defaults(void) conf->acs_num_scans = 5; #endif /* CONFIG_ACS */ + /* The third octet of the country string uses an ASCII space character + * by default to indicate that the regulations encompass all + * environments for the current frequency band in the country. */ + conf->country[2] = ' '; + return conf; } @@ -329,13 +370,7 @@ int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf) ssid->wpa_psk->group = 1; } - if (ssid->wpa_psk_file) { - if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file, - &conf->ssid)) - return -1; - } - - return 0; + return hostapd_config_read_wpa_psk(ssid->wpa_psk_file, &conf->ssid); } @@ -380,10 +415,23 @@ void hostapd_config_free_eap_user(struct hostapd_eap_user *user) hostapd_config_free_radius_attr(user->accept_attr); os_free(user->identity); bin_clear_free(user->password, user->password_len); + bin_clear_free(user->salt, user->salt_len); os_free(user); } +void hostapd_config_free_eap_users(struct hostapd_eap_user *user) +{ + struct hostapd_eap_user *prev_user; + + while (user) { + prev_user = user; + user = user->next; + hostapd_config_free_eap_user(prev_user); + } +} + + static void hostapd_config_free_wep(struct hostapd_wep_keys *keys) { int i; @@ -420,10 +468,38 @@ static void hostapd_config_free_anqp_elem(struct hostapd_bss_config *conf) } -void hostapd_config_free_bss(struct hostapd_bss_config *conf) +static void hostapd_config_free_fils_realms(struct hostapd_bss_config *conf) +{ +#ifdef CONFIG_FILS + struct fils_realm *realm; + + while ((realm = dl_list_first(&conf->fils_realms, struct fils_realm, + list))) { + dl_list_del(&realm->list); + os_free(realm); + } +#endif /* CONFIG_FILS */ +} + + +static void hostapd_config_free_sae_passwords(struct hostapd_bss_config *conf) { - struct hostapd_eap_user *user, *prev_user; + struct sae_password_entry *pw, *tmp; + + pw = conf->sae_passwords; + conf->sae_passwords = NULL; + while (pw) { + tmp = pw; + pw = pw->next; + str_clear_free(tmp->password); + os_free(tmp->identifier); + os_free(tmp); + } +} + +void hostapd_config_free_bss(struct hostapd_bss_config *conf) +{ if (conf == NULL) return; @@ -436,12 +512,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->ssid.vlan_tagged_interface); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ - user = conf->eap_user; - while (user) { - prev_user = user; - user = user->next; - hostapd_config_free_eap_user(prev_user); - } + hostapd_config_free_eap_users(conf->eap_user); os_free(conf->eap_user_sqlite); os_free(conf->eap_req_id_text); @@ -477,7 +548,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) hostapd_config_free_vlan(conf); os_free(conf->time_zone); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP { struct ft_remote_r0kh *r0kh, *r0kh_prev; struct ft_remote_r1kh *r1kh, *r1kh_prev; @@ -498,7 +569,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(r1kh_prev); } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_WPS os_free(conf->wps_pin_requests); @@ -530,6 +601,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->roaming_consortium); os_free(conf->venue_name); + os_free(conf->venue_url); os_free(conf->nai_realm_data); os_free(conf->network_auth_type); os_free(conf->anqp_3gpp_cell_net); @@ -559,17 +631,30 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(p->icons[j]); os_free(p->icons); os_free(p->osu_nai); + os_free(p->osu_nai2); os_free(p->service_desc); } os_free(conf->hs20_osu_providers); } + if (conf->hs20_operator_icon) { + size_t i; + + for (i = 0; i < conf->hs20_operator_icon_count; i++) + os_free(conf->hs20_operator_icon[i]); + os_free(conf->hs20_operator_icon); + } os_free(conf->subscr_remediation_url); + os_free(conf->t_c_filename); + os_free(conf->t_c_server_url); #endif /* CONFIG_HS20 */ wpabuf_free(conf->vendor_elements); wpabuf_free(conf->assocresp_elements); os_free(conf->sae_groups); +#ifdef CONFIG_OWE + os_free(conf->owe_groups); +#endif /* CONFIG_OWE */ os_free(conf->wowlan_triggers); @@ -577,11 +662,22 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) #ifdef CONFIG_TESTING_OPTIONS wpabuf_free(conf->own_ie_override); + wpabuf_free(conf->sae_commit_override); #endif /* CONFIG_TESTING_OPTIONS */ os_free(conf->no_probe_resp_if_seen_on); os_free(conf->no_auth_if_seen_on); + hostapd_config_free_fils_realms(conf); + +#ifdef CONFIG_DPP + os_free(conf->dpp_connector); + wpabuf_free(conf->dpp_netaccesskey); + wpabuf_free(conf->dpp_csign); +#endif /* CONFIG_DPP */ + + hostapd_config_free_sae_passwords(conf); + os_free(conf); } @@ -802,7 +898,7 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, } } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) && (bss->nas_identifier == NULL || os_strlen(bss->nas_identifier) < 1 || @@ -812,7 +908,7 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, "string"); return -1; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211N if (full_config && conf->ieee80211n && @@ -848,6 +944,16 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, wpa_printf(MSG_ERROR, "VHT (IEEE 802.11ac) with WEP is not allowed, disabling VHT capabilities"); } + + if (full_config && conf->ieee80211ac && bss->wpa && + !(bss->wpa_pairwise & WPA_CIPHER_CCMP) && + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | + WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256))) + { + bss->disable_11ac = 1; + wpa_printf(MSG_ERROR, + "VHT (IEEE 802.11ac) with WPA/WPA2 requires CCMP/GCMP to be enabled, disabling VHT capabilities"); + } #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_WPS @@ -866,7 +972,9 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, if (full_config && bss->wps_state && bss->wpa && (!(bss->wpa & 2) || - !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) { + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | + WPA_CIPHER_CCMP_256 | + WPA_CIPHER_GCMP_256)))) { wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without " "WPA2/CCMP/GCMP forced WPS to be disabled"); bss->wps_state = 0; @@ -976,8 +1084,15 @@ void hostapd_set_security_params(struct hostapd_bss_config *bss, if ((bss->wpa & 2) && bss->rsn_pairwise == 0) bss->rsn_pairwise = bss->wpa_pairwise; - bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise, - bss->rsn_pairwise); + if (bss->group_cipher) + bss->wpa_group = bss->group_cipher; + else + bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, + bss->wpa_pairwise, + bss->rsn_pairwise); + if (!bss->wpa_group_rekey_set) + bss->wpa_group_rekey = bss->wpa_group == WPA_CIPHER_TKIP ? + 600 : 86400; if (full_config) { bss->radius->auth_server = bss->radius->auth_servers; diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 8c8f7e286bda..778366d49afe 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -160,6 +160,8 @@ struct hostapd_eap_user { } methods[EAP_MAX_METHODS]; u8 *password; size_t password_len; + u8 *salt; + size_t salt_len; /* non-zero when password is salted */ int phase2; int force_version; unsigned int wildcard_prefix:1; @@ -169,6 +171,7 @@ struct hostapd_eap_user { unsigned int macacl:1; int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ struct hostapd_radius_attr *accept_attr; + u32 t_c_timestamp; }; struct hostapd_radius_attr { @@ -201,6 +204,12 @@ struct hostapd_lang_string { u8 name[252]; }; +struct hostapd_venue_url { + u8 venue_number; + u8 url_len; + u8 url[254]; +}; + #define MAX_NAI_REALMS 10 #define MAX_NAI_REALMLEN 255 #define MAX_NAI_EAP_METHODS 5 @@ -224,6 +233,18 @@ struct anqp_element { struct wpabuf *payload; }; +struct fils_realm { + struct dl_list list; + u8 hash[2]; + char realm[]; +}; + +struct sae_password_entry { + struct sae_password_entry *next; + char *password; + char *identifier; + u8 peer_addr[ETH_ALEN]; +}; /** * struct hostapd_bss_config - Per-BSS configuration @@ -242,7 +263,8 @@ struct hostapd_bss_config { int max_num_sta; /* maximum number of STAs in station table */ int dtim_period; - int bss_load_update_period; + unsigned int bss_load_update_period; + unsigned int chan_util_avg_period; int ieee802_1x; /* use IEEE 802.1X */ int eapol_version; @@ -287,7 +309,7 @@ struct hostapd_bss_config { char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast * frames */ - enum { + enum macaddr_acl { ACCEPT_UNLESS_DENIED = 0, DENY_UNLESS_ACCEPTED = 1, USE_EXTERNAL_RADIUS_AUTH = 2 @@ -319,27 +341,37 @@ struct hostapd_bss_config { PSK_RADIUS_REQUIRED = 2 } wpa_psk_radius; int wpa_pairwise; + int group_cipher; /* wpa_group value override from configuation */ int wpa_group; int wpa_group_rekey; + int wpa_group_rekey_set; int wpa_strict_rekey; int wpa_gmk_rekey; int wpa_ptk_rekey; + u32 wpa_group_update_count; + u32 wpa_pairwise_update_count; + int wpa_disable_eapol_key_retries; int rsn_pairwise; int rsn_preauth; char *rsn_preauth_interfaces; - int peerkey; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP /* IEEE 802.11r - Fast BSS Transition */ u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 r1_key_holder[FT_R1KH_ID_LEN]; - u32 r0_key_lifetime; + u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */ + int rkh_pos_timeout; + int rkh_neg_timeout; + int rkh_pull_timeout; /* ms */ + int rkh_pull_retries; 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 ft_psk_generate_local; + int r1_max_key_lifetime; +#endif /* CONFIG_IEEE80211R_AP */ char *ctrl_interface; /* directory for UNIX domain sockets */ #ifndef CONFIG_NATIVE_WINDOWS @@ -353,6 +385,7 @@ struct hostapd_bss_config { char *private_key_passwd; int check_crl; unsigned int tls_session_lifetime; + unsigned int tls_flags; char *ocsp_stapling_response; char *ocsp_stapling_response_multi; char *dh_file; @@ -464,6 +497,7 @@ struct hostapd_bss_config { int time_advertisement; char *time_zone; int wnm_sleep_mode; + int wnm_sleep_mode_no_keys; int bss_transition; /* IEEE 802.11u - Interworking */ @@ -486,6 +520,10 @@ struct hostapd_bss_config { unsigned int venue_name_count; struct hostapd_lang_string *venue_name; + /* Venue URL duples */ + unsigned int venue_url_count; + struct hostapd_venue_url *venue_url; + /* IEEE 802.11u - Network Authentication Type */ u8 *network_auth_type; size_t network_auth_type_len; @@ -508,7 +546,7 @@ struct hostapd_bss_config { struct dl_list anqp_elem; /* list of struct anqp_element */ u16 gas_comeback_delay; - int gas_frag_limit; + size_t gas_frag_limit; int gas_address3; u8 qos_map_set[16 + 2 * 21]; @@ -547,13 +585,20 @@ struct hostapd_bss_config { char **icons; size_t icons_count; char *osu_nai; + char *osu_nai2; unsigned int service_desc_count; struct hostapd_lang_string *service_desc; } *hs20_osu_providers, *last_osu; size_t hs20_osu_providers_count; + size_t hs20_osu_providers_nai_count; + char **hs20_operator_icon; + size_t hs20_operator_icon_count; unsigned int hs20_deauth_req_timeout; char *subscr_remediation_url; u8 subscr_remediation_method; + char *t_c_filename; + u32 t_c_timestamp; + char *t_c_server_url; #endif /* CONFIG_HS20 */ u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */ @@ -566,7 +611,10 @@ struct hostapd_bss_config { struct wpabuf *assocresp_elements; unsigned int sae_anti_clogging_threshold; + unsigned int sae_sync; + int sae_require_mfp; int *sae_groups; + struct sae_password_entry *sae_passwords; char *wowlan_triggers; /* Wake-on-WLAN triggers */ @@ -574,6 +622,8 @@ struct hostapd_bss_config { u8 bss_load_test[5]; u8 bss_load_test_set; struct wpabuf *own_ie_override; + int sae_reflection_attack; + struct wpabuf *sae_commit_override; #endif /* CONFIG_TESTING_OPTIONS */ #define MESH_ENABLED BIT(0) @@ -591,12 +641,71 @@ struct hostapd_bss_config { #ifdef CONFIG_MBO int mbo_enabled; + /** + * oce - Enable OCE in AP and/or STA-CFON mode + * - BIT(0) is Reserved + * - Set BIT(1) to enable OCE in STA-CFON mode + * - Set BIT(2) to enable OCE in AP mode + */ + unsigned int oce; + int mbo_cell_data_conn_pref; #endif /* CONFIG_MBO */ int ftm_responder; int ftm_initiator; + +#ifdef CONFIG_FILS + u8 fils_cache_id[FILS_CACHE_ID_LEN]; + int fils_cache_id_set; + struct dl_list fils_realms; /* list of struct fils_realm */ + int fils_dh_group; + struct hostapd_ip_addr dhcp_server; + int dhcp_rapid_commit_proxy; + unsigned int fils_hlp_wait_time; + u16 dhcp_server_port; + u16 dhcp_relay_port; +#endif /* CONFIG_FILS */ + + int multicast_to_unicast; + + int broadcast_deauth; + +#ifdef CONFIG_DPP + char *dpp_connector; + struct wpabuf *dpp_netaccesskey; + unsigned int dpp_netaccesskey_expiry; + struct wpabuf *dpp_csign; +#endif /* CONFIG_DPP */ + +#ifdef CONFIG_OWE + macaddr owe_transition_bssid; + u8 owe_transition_ssid[SSID_MAX_LEN]; + size_t owe_transition_ssid_len; + char owe_transition_ifname[IFNAMSIZ + 1]; + int *owe_groups; +#endif /* CONFIG_OWE */ + + int coloc_intf_reporting; +}; + +/** + * struct he_phy_capabilities_info - HE PHY capabilities + */ +struct he_phy_capabilities_info { + Boolean he_su_beamformer; + Boolean he_su_beamformee; + Boolean he_mu_beamformer; }; +/** + * struct he_operation - HE operation + */ +struct he_operation { + u8 he_bss_color; + u8 he_default_pe_duration; + u8 he_twt_required; + u8 he_rts_threshold; +}; /** * struct hostapd_config - Per-radio interface configuration @@ -612,6 +721,7 @@ struct hostapd_config { u8 channel; u8 acs; struct wpa_freq_range_list acs_ch_list; + int acs_exclude_dfs; enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */ enum { LONG_PREAMBLE = 0, @@ -620,6 +730,8 @@ struct hostapd_config { int *supported_rates; int *basic_rates; + unsigned int beacon_rate; + enum beacon_rate_type rate_type; const struct wpa_driver_ops *driver; char *driver_params; @@ -635,6 +747,9 @@ struct hostapd_config { * ' ' (ascii 32): all environments * 'O': Outdoor environemnt only * 'I': Indoor environment only + * 'X': Used with noncountry entity ("XXX") + * 0x00..0x31: identifying IEEE 802.11 standard + * Annex E table (0x04 = global table) */ int ieee80211d; @@ -675,6 +790,7 @@ struct hostapd_config { u8 vht_oper_chwidth; u8 vht_oper_centr_freq_seg0_idx; u8 vht_oper_centr_freq_seg1_idx; + u8 ht40_plus_minus_allowed; /* Use driver-generated interface addresses when adding multiple BSSs */ u8 use_driver_iface_addr; @@ -707,6 +823,18 @@ struct hostapd_config { struct wpabuf *lci; struct wpabuf *civic; + int stationary_ap; + + int ieee80211ax; +#ifdef CONFIG_IEEE80211AX + struct he_phy_capabilities_info he_phy_capab; + struct he_operation he_op; +#endif /* CONFIG_IEEE80211AX */ + + /* VHT enable/disable config from CHAN_SWITCH */ +#define CH_SWITCH_VHT_ENABLED BIT(0) +#define CH_SWITCH_VHT_DISABLED BIT(1) + unsigned int ch_switch_vht_config; }; @@ -714,6 +842,7 @@ int hostapd_mac_comp(const void *a, const void *b); struct hostapd_config * hostapd_config_defaults(void); void hostapd_config_defaults_bss(struct hostapd_bss_config *bss); void hostapd_config_free_eap_user(struct hostapd_eap_user *user); +void hostapd_config_free_eap_users(struct hostapd_eap_user *user); void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p); void hostapd_config_free_bss(struct hostapd_bss_config *conf); void hostapd_config_free(struct hostapd_config *conf); diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index f1394654d3a8..067cf863e84c 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -19,6 +19,7 @@ #include "ap_config.h" #include "p2p_hostapd.h" #include "hs20.h" +#include "wpa_auth.h" #include "ap_drv_ops.h" @@ -99,6 +100,13 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, goto fail; #endif /* CONFIG_FST */ +#ifdef CONFIG_FILS + pos = hostapd_eid_fils_indic(hapd, buf, 0); + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; +#endif /* CONFIG_FILS */ + if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 || add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0) goto fail; @@ -168,7 +176,8 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, #endif /* CONFIG_HS20 */ #ifdef CONFIG_MBO - if (hapd->conf->mbo_enabled) { + if (hapd->conf->mbo_enabled || + OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) { pos = hostapd_eid_mbo(hapd, buf, sizeof(buf)); if (add_buf_data(&beacon, buf, pos - buf) < 0 || add_buf_data(&proberesp, buf, pos - buf) < 0 || @@ -177,6 +186,13 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, } #endif /* CONFIG_MBO */ +#ifdef CONFIG_OWE + pos = hostapd_eid_owe_trans(hapd, buf, sizeof(buf)); + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; +#endif /* CONFIG_OWE */ + add_buf(&beacon, hapd->conf->vendor_elements); add_buf(&proberesp, hapd->conf->vendor_elements); add_buf(&assocresp, hapd->conf->assocresp_elements); @@ -340,10 +356,44 @@ int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, u16 seq, u16 status, const u8 *ie, size_t len) { + struct wpa_driver_sta_auth_params params; +#ifdef CONFIG_FILS + struct sta_info *sta; +#endif /* CONFIG_FILS */ + if (hapd->driver == NULL || hapd->driver->sta_auth == NULL) return 0; - return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr, - seq, status, ie, len); + + os_memset(¶ms, 0, sizeof(params)); + +#ifdef CONFIG_FILS + sta = ap_get_sta(hapd, addr); + if (!sta) { + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not found for sta_auth processing", + MAC2STR(addr)); + return 0; + } + + if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) { + params.fils_auth = 1; + wpa_auth_get_fils_aead_params(sta->wpa_sm, params.fils_anonce, + params.fils_snonce, + params.fils_kek, + ¶ms.fils_kek_len); + } +#endif /* CONFIG_FILS */ + + params.own_addr = hapd->own_addr; + params.addr = addr; + params.seq = seq; + params.status = status; + params.ie = ie; + params.len = len; + + return hapd->driver->sta_auth(hapd->drv_priv, ¶ms); } @@ -554,13 +604,13 @@ int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, struct hostapd_hw_modes * hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, - u16 *flags) + u16 *flags, u8 *dfs_domain) { if (hapd->driver == NULL || hapd->driver->get_hw_feature_data == NULL) return NULL; return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes, - flags); + flags, dfs_domain); } @@ -694,6 +744,15 @@ int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, sta = ap_get_sta(hapd, dst); if (!sta || !(sta->flags & WLAN_STA_ASSOC)) bssid = wildcard_bssid; + } else if (is_broadcast_ether_addr(dst) && + len > 0 && data[0] == WLAN_ACTION_PUBLIC) { + /* + * The only current use case of Public Action frames with + * broadcast destination address is DPP PKEX. That case is + * directing all devices and not just the STAs within the BSS, + * so have to use the wildcard BSSID value. + */ + bssid = wildcard_bssid; } return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst, hapd->own_addr, bssid, data, len, 0); @@ -774,7 +833,9 @@ static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd, if ((acs_ch_list_all || freq_range_list_includes(&hapd->iface->conf->acs_ch_list, chan->chan)) && - !(chan->flag & HOSTAPD_CHAN_DISABLED)) + !(chan->flag & HOSTAPD_CHAN_DISABLED) && + !(hapd->iface->conf->acs_exclude_dfs && + (chan->flag & HOSTAPD_CHAN_RADAR))) int_array_add_unique(freq_list, chan->freq); } } @@ -829,6 +890,9 @@ int hostapd_drv_do_acs(struct hostapd_data *hapd) &hapd->iface->conf->acs_ch_list, chan->chan)) continue; + if (hapd->iface->conf->acs_exclude_dfs && + (chan->flag & HOSTAPD_CHAN_RADAR)) + continue; if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) { channels[num_channels++] = chan->chan; int_array_add_unique(&freq_list, chan->freq); diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 0bb7954ec061..db93fde7d6e3 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -72,7 +72,7 @@ int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, int cw_min, int cw_max, int burst_time); struct hostapd_hw_modes * hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, - u16 *flags); + u16 *flags, u8 *dfs_domain); int hostapd_driver_commit(struct hostapd_data *hapd); int hostapd_drv_none(struct hostapd_data *hapd); int hostapd_driver_scan(struct hostapd_data *hapd, @@ -103,6 +103,14 @@ int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *data, size_t len); +static inline void +hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd) +{ + if (!hapd->driver || !hapd->driver->send_action_cancel_wait || + !hapd->drv_priv) + return; + hapd->driver->send_action_cancel_wait(hapd->drv_priv); +} int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, u16 auth_alg); int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, @@ -274,8 +282,9 @@ static inline const char * hostapd_drv_get_radio_name(struct hostapd_data *hapd) static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd, struct csa_settings *settings) { - if (hapd->driver == NULL || hapd->driver->switch_channel == NULL) - return -ENOTSUP; + if (hapd->driver == NULL || hapd->driver->switch_channel == NULL || + hapd->drv_priv == NULL) + return -1; return hapd->driver->switch_channel(hapd->drv_priv, settings); } diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c index e7308a01d743..db8a26759c28 100644 --- a/src/ap/ap_mlme.c +++ b/src/ap/ap_mlme.c @@ -57,7 +57,11 @@ void mlme_authenticate_indication(struct hostapd_data *hapd, HOSTAPD_LEVEL_DEBUG, "MLME-AUTHENTICATE.indication(" MACSTR ", %s)", MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg)); - if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP)) + if (sta->auth_alg != WLAN_AUTH_FT && + sta->auth_alg != WLAN_AUTH_FILS_SK && + sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && + sta->auth_alg != WLAN_AUTH_FILS_PK && + !(sta->flags & WLAN_STA_MFP)) mlme_deletekeys_request(hapd, sta); ap_sta_clear_disconnect_timeouts(hapd, sta); } @@ -105,7 +109,10 @@ void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta) HOSTAPD_LEVEL_DEBUG, "MLME-ASSOCIATE.indication(" MACSTR ")", MAC2STR(sta->addr)); - if (sta->auth_alg != WLAN_AUTH_FT) + if (sta->auth_alg != WLAN_AUTH_FT && + sta->auth_alg != WLAN_AUTH_FILS_SK && + sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && + sta->auth_alg != WLAN_AUTH_FILS_PK) mlme_deletekeys_request(hapd, sta); ap_sta_clear_disconnect_timeouts(hapd, sta); } @@ -130,7 +137,10 @@ void mlme_reassociate_indication(struct hostapd_data *hapd, HOSTAPD_LEVEL_DEBUG, "MLME-REASSOCIATE.indication(" MACSTR ")", MAC2STR(sta->addr)); - if (sta->auth_alg != WLAN_AUTH_FT) + if (sta->auth_alg != WLAN_AUTH_FT && + sta->auth_alg != WLAN_AUTH_FILS_SK && + sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && + sta->auth_alg != WLAN_AUTH_FILS_PK) mlme_deletekeys_request(hapd, sta); ap_sta_clear_disconnect_timeouts(hapd, sta); } diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index cdb49cdd9d32..95d004ed2b16 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -71,19 +71,26 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, } if (eap_user->password) { - user->password = os_malloc(eap_user->password_len); + user->password = os_memdup(eap_user->password, + eap_user->password_len); if (user->password == NULL) goto out; - os_memcpy(user->password, eap_user->password, - eap_user->password_len); user->password_len = eap_user->password_len; user->password_hash = eap_user->password_hash; + if (eap_user->salt && eap_user->salt_len) { + user->salt = os_memdup(eap_user->salt, + eap_user->salt_len); + if (!user->salt) + goto out; + user->salt_len = eap_user->salt_len; + } } user->force_version = eap_user->force_version; user->macacl = eap_user->macacl; user->ttls_auth = eap_user->ttls_auth; user->remediation = eap_user->remediation; user->accept_attr = eap_user->accept_attr; + user->t_c_timestamp = eap_user->t_c_timestamp; rv = 0; out: @@ -129,10 +136,12 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) #ifdef CONFIG_HS20 srv.subscr_remediation_url = conf->subscr_remediation_url; srv.subscr_remediation_method = conf->subscr_remediation_method; + srv.t_c_server_url = conf->t_c_server_url; #endif /* CONFIG_HS20 */ srv.erp = conf->eap_server_erp; srv.erp_domain = conf->erp_domain; srv.tls_session_lifetime = conf->tls_session_lifetime; + srv.tls_flags = conf->tls_flags; hapd->radius_srv = radius_server_init(&srv); if (hapd->radius_srv == NULL) { @@ -146,6 +155,40 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) #endif /* RADIUS_SERVER */ +#ifdef EAP_TLS_FUNCS +static void authsrv_tls_event(void *ctx, enum tls_event ev, + union tls_event_data *data) +{ + switch (ev) { + case TLS_CERT_CHAIN_SUCCESS: + wpa_printf(MSG_DEBUG, "authsrv: remote certificate verification success"); + break; + case TLS_CERT_CHAIN_FAILURE: + wpa_printf(MSG_INFO, "authsrv: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'", + data->cert_fail.reason, + data->cert_fail.depth, + data->cert_fail.subject, + data->cert_fail.reason_txt); + break; + case TLS_PEER_CERTIFICATE: + wpa_printf(MSG_DEBUG, "authsrv: peer certificate: depth=%d serial_num=%s subject=%s", + data->peer_cert.depth, + data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A", + data->peer_cert.subject); + break; + case TLS_ALERT: + if (data->alert.is_local) + wpa_printf(MSG_DEBUG, "authsrv: local TLS alert: %s", + data->alert.description); + else + wpa_printf(MSG_DEBUG, "authsrv: remote TLS alert: %s", + data->alert.description); + break; + } +} +#endif /* EAP_TLS_FUNCS */ + + int authsrv_init(struct hostapd_data *hapd) { #ifdef EAP_TLS_FUNCS @@ -157,6 +200,9 @@ int authsrv_init(struct hostapd_data *hapd) os_memset(&conf, 0, sizeof(conf)); conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; + conf.tls_flags = hapd->conf->tls_flags; + conf.event_cb = authsrv_tls_event; + conf.cb_ctx = hapd; hapd->ssl_ctx = tls_init(&conf); if (hapd->ssl_ctx == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize TLS"); diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 233320d2e978..59bd4af395d7 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -16,6 +16,7 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/hw_features_common.h" +#include "common/wpa_ctrl.h" #include "wps/wps_defs.h" #include "p2p/p2p.h" #include "hostapd.h" @@ -30,6 +31,7 @@ #include "hs20.h" #include "dfs.h" #include "taxonomy.h" +#include "ieee802_11_auth.h" #ifdef NEED_AP_MLME @@ -392,7 +394,15 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, 2 + sizeof(struct ieee80211_vht_operation); } +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax) { + buflen += 3 + sizeof(struct ieee80211_he_capabilities) + + 3 + sizeof(struct ieee80211_he_operation); + } +#endif /* CONFIG_IEEE80211AX */ + buflen += hostapd_mbo_ie_len(hapd); + buflen += hostapd_eid_owe_trans_len(hapd); resp = os_zalloc(buflen); if (resp == NULL) @@ -443,8 +453,9 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, /* Extended supported rates */ pos = hostapd_eid_ext_supp_rates(hapd, pos); - /* RSN, MDIE, WPA */ - pos = hostapd_eid_wpa(hapd, pos, epos - pos); + /* RSN, MDIE */ + if (hapd->conf->wpa != WPA_PROTO_WPA) + pos = hostapd_eid_wpa(hapd, pos, epos - pos); pos = hostapd_eid_bss_load(hapd, pos, epos - pos); @@ -491,10 +502,26 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_eid_txpower_envelope(hapd, pos); pos = hostapd_eid_wb_chsw_wrapper(hapd, pos); } +#endif /* CONFIG_IEEE80211AC */ + + pos = hostapd_eid_fils_indic(hapd, pos, 0); + +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax) { + pos = hostapd_eid_he_capab(hapd, pos); + pos = hostapd_eid_he_operation(hapd, pos); + } +#endif /* CONFIG_IEEE80211AX */ + +#ifdef CONFIG_IEEE80211AC if (hapd->conf->vendor_vht) pos = hostapd_eid_vendor_vht(hapd, pos); #endif /* CONFIG_IEEE80211AC */ + /* WPA */ + if (hapd->conf->wpa == WPA_PROTO_WPA) + pos = hostapd_eid_wpa(hapd, pos, epos - pos); + /* Wi-Fi Alliance WMM */ pos = hostapd_eid_wmm(hapd, pos); @@ -526,6 +553,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, #endif /* CONFIG_HS20 */ pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos); + pos = hostapd_eid_owe_trans(hapd, pos, (u8 *) resp + buflen - pos); if (hapd->conf->vendor_elements) { os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements), @@ -618,7 +646,7 @@ static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface, } -void sta_track_add(struct hostapd_iface *iface, const u8 *addr) +void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal) { struct hostapd_sta_info *info; @@ -628,6 +656,7 @@ void sta_track_add(struct hostapd_iface *iface, const u8 *addr) dl_list_del(&info->list); dl_list_add_tail(&iface->sta_seen, &info->list); os_get_reltime(&info->last_seen); + info->ssi_signal = ssi_signal; return; } @@ -637,6 +666,7 @@ void sta_track_add(struct hostapd_iface *iface, const u8 *addr) return; os_memcpy(info->addr, addr, ETH_ALEN); os_get_reltime(&info->last_seen); + info->ssi_signal = ssi_signal; if (iface->num_sta_seen >= iface->conf->track_sta_max_num) { /* Expire oldest entry to make room for a new one */ @@ -707,14 +737,30 @@ void handle_probe_req(struct hostapd_data *hapd, int ret; u16 csa_offs[2]; size_t csa_offs_len; + u32 session_timeout, acct_interim_interval; + struct vlan_description vlan_id; + struct hostapd_sta_wpa_psk_short *psk = NULL; + char *identity = NULL; + char *radius_cui = NULL; if (len < IEEE80211_HDRLEN) return; ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN; if (hapd->iconf->track_sta_max_num) - sta_track_add(hapd->iface, mgmt->sa); + sta_track_add(hapd->iface, mgmt->sa, ssi_signal); ie_len = len - IEEE80211_HDRLEN; + ret = ieee802_11_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len, + &session_timeout, + &acct_interim_interval, &vlan_id, + &psk, &identity, &radius_cui, 1); + if (ret == HOSTAPD_ACL_REJECT) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Ignore Probe Request frame from " MACSTR + " due to ACL reject ", MAC2STR(mgmt->sa)); + return; + } + for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, mgmt->sa, mgmt->da, mgmt->bssid, @@ -909,6 +955,9 @@ void handle_probe_req(struct hostapd_data *hapd, } #endif /* CONFIG_TESTING_OPTIONS */ + wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR + " signal=%d", MAC2STR(mgmt->sa), ssi_signal); + resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL, &resp_len); if (resp == NULL) @@ -1033,7 +1082,15 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, } #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax) { + tail_len += 3 + sizeof(struct ieee80211_he_capabilities) + + 3 + sizeof(struct ieee80211_he_operation); + } +#endif /* CONFIG_IEEE80211AX */ + tail_len += hostapd_mbo_ie_len(hapd); + tail_len += hostapd_eid_owe_trans_len(hapd); tailpos = tail = os_malloc(tail_len); if (head == NULL || tail == NULL) { @@ -1100,9 +1157,11 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, /* Extended supported rates */ tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos); - /* RSN, MDIE, WPA */ - tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - - tailpos); + /* RSN, MDIE */ + if (hapd->conf->wpa != WPA_PROTO_WPA) + tailpos = hostapd_eid_wpa(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - + tailpos); tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - @@ -1155,10 +1214,28 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tailpos = hostapd_eid_txpower_envelope(hapd, tailpos); tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos); } +#endif /* CONFIG_IEEE80211AC */ + + tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0); + +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax) { + tailpos = hostapd_eid_he_capab(hapd, tailpos); + tailpos = hostapd_eid_he_operation(hapd, tailpos); + } +#endif /* CONFIG_IEEE80211AX */ + +#ifdef CONFIG_IEEE80211AC if (hapd->conf->vendor_vht) tailpos = hostapd_eid_vendor_vht(hapd, tailpos); #endif /* CONFIG_IEEE80211AC */ + /* WPA */ + if (hapd->conf->wpa == WPA_PROTO_WPA) + tailpos = hostapd_eid_wpa(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - + tailpos); + /* Wi-Fi Alliance WMM */ tailpos = hostapd_eid_wmm(hapd, tailpos); @@ -1189,6 +1266,8 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #endif /* CONFIG_HS20 */ tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos); + tailpos = hostapd_eid_owe_trans(hapd, tailpos, + tail + tail_len - tailpos); if (hapd->conf->vendor_elements) { os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements), @@ -1211,6 +1290,8 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, params->dtim_period = hapd->conf->dtim_period; params->beacon_int = hapd->iconf->beacon_int; params->basic_rates = hapd->iface->basic_rates; + params->beacon_rate = hapd->iconf->beacon_rate; + params->rate_type = hapd->iconf->rate_type; params->ssid = hapd->conf->ssid.ssid; params->ssid_len = hapd->conf->ssid.ssid_len; if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) == @@ -1274,6 +1355,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, params->osen = 1; } #endif /* CONFIG_HS20 */ + params->multicast_to_unicast = hapd->conf->multicast_to_unicast; params->pbss = hapd->conf->pbss; return 0; } diff --git a/src/ap/beacon.h b/src/ap/beacon.h index fc711815cf65..a26e30879cf5 100644 --- a/src/ap/beacon.h +++ b/src/ap/beacon.h @@ -21,7 +21,7 @@ int ieee802_11_update_beacons(struct hostapd_iface *iface); int ieee802_11_build_ap_params(struct hostapd_data *hapd, struct wpa_driver_ap_params *params); void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params); -void sta_track_add(struct hostapd_iface *iface, const u8 *addr); +void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal); void sta_track_del(struct hostapd_sta_info *info); void sta_track_expire(struct hostapd_iface *iface, int force); struct hostapd_data * diff --git a/src/ap/bss_load.c b/src/ap/bss_load.c index fb639423230c..725d3cd3469b 100644 --- a/src/ap/bss_load.c +++ b/src/ap/bss_load.c @@ -16,11 +16,35 @@ #include "beacon.h" +static int get_bss_load_update_timeout(struct hostapd_data *hapd, + unsigned int *sec, unsigned int *usec) +{ + unsigned int update_period = hapd->conf->bss_load_update_period; + unsigned int beacon_int = hapd->iconf->beacon_int; + unsigned int update_timeout; + + if (!update_period || !beacon_int) { + wpa_printf(MSG_ERROR, + "BSS Load: Invalid BSS load update configuration (period=%u beacon_int=%u)", + update_period, beacon_int); + return -1; + } + + update_timeout = update_period * beacon_int; + + *sec = ((update_timeout / 1000) * 1024) / 1000; + *usec = (update_timeout % 1000) * 1024; + + return 0; +} + + static void update_channel_utilization(void *eloop_data, void *user_data) { struct hostapd_data *hapd = eloop_data; unsigned int sec, usec; int err; + struct hostapd_iface *iface = hapd->iface; if (!(hapd->beacon_set_done && hapd->started)) return; @@ -33,8 +57,24 @@ static void update_channel_utilization(void *eloop_data, void *user_data) ieee802_11_set_beacon(hapd); - sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000; - usec = (hapd->bss_load_update_timeout % 1000) * 1024; + if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0) + return; + + if (hapd->conf->chan_util_avg_period) { + iface->chan_util_samples_sum += iface->channel_utilization; + iface->chan_util_num_sample_periods += + hapd->conf->bss_load_update_period; + if (iface->chan_util_num_sample_periods >= + hapd->conf->chan_util_avg_period) { + iface->chan_util_average = + iface->chan_util_samples_sum / + (iface->chan_util_num_sample_periods / + hapd->conf->bss_load_update_period); + iface->chan_util_samples_sum = 0; + iface->chan_util_num_sample_periods = 0; + } + } + eloop_register_timeout(sec, usec, update_channel_utilization, hapd, NULL); } @@ -42,17 +82,11 @@ static void update_channel_utilization(void *eloop_data, void *user_data) int bss_load_update_init(struct hostapd_data *hapd) { - struct hostapd_bss_config *conf = hapd->conf; - struct hostapd_config *iconf = hapd->iconf; unsigned int sec, usec; - if (!conf->bss_load_update_period || !iconf->beacon_int) + if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0) return -1; - hapd->bss_load_update_timeout = conf->bss_load_update_period * - iconf->beacon_int; - sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000; - usec = (hapd->bss_load_update_timeout % 1000) * 1024; eloop_register_timeout(sec, usec, update_channel_utilization, hapd, NULL); return 0; diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index 3680fda3153f..21b813ee18d7 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -26,23 +26,141 @@ #include "taxonomy.h" +static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen, + size_t curr_len, const u8 *mcs_set) +{ + int ret; + size_t len = curr_len; + + ret = os_snprintf(buf + len, buflen - len, + "ht_mcs_bitmask="); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + /* 77 first bits (+ 3 reserved bits) */ + len += wpa_snprintf_hex(buf + len, buflen - len, mcs_set, 10); + + ret = os_snprintf(buf + len, buflen - len, "\n"); + if (os_snprintf_error(buflen - len, ret)) + return curr_len; + len += ret; + + return len; +} + + static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen) { struct hostap_sta_driver_data data; int ret; + int len = 0; if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0) return 0; ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n" - "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n", + "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n" + "signal=%d\n", data.rx_packets, data.tx_packets, - data.rx_bytes, data.tx_bytes, data.inactive_msec); + data.rx_bytes, data.tx_bytes, data.inactive_msec, + data.signal); if (os_snprintf_error(buflen, ret)) return 0; - return ret; + len += ret; + + ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu", + data.current_rx_rate); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + if (data.flags & STA_DRV_DATA_RX_MCS) { + ret = os_snprintf(buf + len, buflen - len, " mcs %u", + data.rx_mcs); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_RX_VHT_MCS) { + ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u", + data.rx_vhtmcs); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_RX_VHT_NSS) { + ret = os_snprintf(buf + len, buflen - len, " vhtnss %u", + data.rx_vht_nss); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_RX_SHORT_GI) { + ret = os_snprintf(buf + len, buflen - len, " shortGI"); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + ret = os_snprintf(buf + len, buflen - len, "\n"); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + + ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu", + data.current_tx_rate); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + if (data.flags & STA_DRV_DATA_TX_MCS) { + ret = os_snprintf(buf + len, buflen - len, " mcs %u", + data.tx_mcs); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_TX_VHT_MCS) { + ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u", + data.tx_vhtmcs); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_TX_VHT_NSS) { + ret = os_snprintf(buf + len, buflen - len, " vhtnss %u", + data.tx_vht_nss); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_TX_SHORT_GI) { + ret = os_snprintf(buf + len, buflen - len, " shortGI"); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + ret = os_snprintf(buf + len, buflen - len, "\n"); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + + if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) { + ret = os_snprintf(buf + len, buflen - len, + "rx_vht_mcs_map=%04x\n" + "tx_vht_mcs_map=%04x\n", + le_to_host16(sta->vht_capabilities-> + vht_supported_mcs_set.rx_map), + le_to_host16(sta->vht_capabilities-> + vht_supported_mcs_set.tx_map)); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + + if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) { + len = hostapd_write_ht_mcs_bitmask(buf, buflen, len, + sta->ht_capabilities-> + supported_mcs_set); + } + + if (data.flags & STA_DRV_DATA_LAST_ACK_RSSI) { + ret = os_snprintf(buf + len, buflen - len, + "last_ack_signal=%d\n", data.last_ack_rssi); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + + return len; } @@ -176,6 +294,53 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, len += os_snprintf(buf + len, buflen - len, "\n"); } + if (sta->power_capab) { + ret = os_snprintf(buf + len, buflen - len, + "min_txpower=%d\n" + "max_txpower=%d\n", + sta->min_tx_power, sta->max_tx_power); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + +#ifdef CONFIG_IEEE80211AC + if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) { + res = os_snprintf(buf + len, buflen - len, + "vht_caps_info=0x%08x\n", + le_to_host32(sta->vht_capabilities-> + vht_capabilities_info)); + if (!os_snprintf_error(buflen - len, res)) + len += res; + } +#endif /* CONFIG_IEEE80211AC */ + +#ifdef CONFIG_IEEE80211N + if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) { + res = os_snprintf(buf + len, buflen - len, + "ht_caps_info=0x%04x\n", + le_to_host16(sta->ht_capabilities-> + ht_capabilities_info)); + if (!os_snprintf_error(buflen - len, res)) + len += res; + } +#endif /* CONFIG_IEEE80211N */ + + if (sta->ext_capability && + buflen - len > (unsigned) (11 + 2 * sta->ext_capability[0])) { + len += os_snprintf(buf + len, buflen - len, "ext_capab="); + len += wpa_snprintf_hex(buf + len, buflen - len, + sta->ext_capability + 1, + sta->ext_capability[0]); + len += os_snprintf(buf + len, buflen - len, "\n"); + } + + if (sta->flags & WLAN_STA_WDS && sta->ifname_wds) { + ret = os_snprintf(buf + len, buflen - len, + "wds_sta_ifname=%s\n", sta->ifname_wds); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + return len; } @@ -477,7 +642,8 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, size_t buflen) { struct hostapd_iface *iface = hapd->iface; - int len = 0, ret; + struct hostapd_hw_modes *mode = iface->current_mode; + int len = 0, ret, j; size_t i; ret = os_snprintf(buf + len, buflen - len, @@ -537,13 +703,17 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, "channel=%u\n" "secondary_channel=%d\n" "ieee80211n=%d\n" - "ieee80211ac=%d\n", + "ieee80211ac=%d\n" + "beacon_int=%u\n" + "dtim_period=%d\n", iface->conf->channel, iface->conf->ieee80211n && !hapd->conf->disable_11n ? iface->conf->secondary_channel : 0, iface->conf->ieee80211n && !hapd->conf->disable_11n, iface->conf->ieee80211ac && - !hapd->conf->disable_11ac); + !hapd->conf->disable_11ac, + iface->conf->beacon_int, + hapd->conf->dtim_period); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -551,15 +721,76 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, ret = os_snprintf(buf + len, buflen - len, "vht_oper_chwidth=%d\n" "vht_oper_centr_freq_seg0_idx=%d\n" - "vht_oper_centr_freq_seg1_idx=%d\n", + "vht_oper_centr_freq_seg1_idx=%d\n" + "vht_caps_info=%08x\n", iface->conf->vht_oper_chwidth, iface->conf->vht_oper_centr_freq_seg0_idx, - iface->conf->vht_oper_centr_freq_seg1_idx); + iface->conf->vht_oper_centr_freq_seg1_idx, + iface->conf->vht_capab); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; } + if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac && mode) { + u16 rxmap = WPA_GET_LE16(&mode->vht_mcs_set[0]); + u16 txmap = WPA_GET_LE16(&mode->vht_mcs_set[4]); + + ret = os_snprintf(buf + len, buflen - len, + "rx_vht_mcs_map=%04x\n" + "tx_vht_mcs_map=%04x\n", + rxmap, txmap); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + + if (iface->conf->ieee80211n && !hapd->conf->disable_11n) { + ret = os_snprintf(buf + len, buflen - len, + "ht_caps_info=%04x\n", + hapd->iconf->ht_capab); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + + if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) { + len = hostapd_write_ht_mcs_bitmask(buf, buflen, len, + mode->mcs_set); + } + + if (iface->current_rates && iface->num_rates) { + ret = os_snprintf(buf + len, buflen - len, "supported_rates="); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + for (j = 0; j < iface->num_rates; j++) { + ret = os_snprintf(buf + len, buflen - len, "%s%02x", + j > 0 ? " " : "", + iface->current_rates[j].rate / 5); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + ret = os_snprintf(buf + len, buflen - len, "\n"); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + + for (j = 0; mode && j < mode->num_channels; j++) { + if (mode->channels[j].freq == iface->freq) { + ret = os_snprintf(buf + len, buflen - len, + "max_txpower=%u\n", + mode->channels[j].max_tx_power); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + break; + } + } + for (i = 0; i < iface->num_bss; i++) { struct hostapd_data *bss = iface->bss[i]; ret = os_snprintf(buf + len, buflen - len, @@ -578,6 +809,15 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, len += ret; } + if (hapd->conf->chan_util_avg_period) { + ret = os_snprintf(buf + len, buflen - len, + "chan_util_avg=%u\n", + iface->chan_util_average); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + return len; } @@ -639,3 +879,108 @@ void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd) { wpa_auth_pmksa_flush(hapd->wpa_auth); } + + +int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd) +{ + u8 spa[ETH_ALEN]; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN_MAX]; + size_t pmk_len; + char *pos, *pos2; + int akmp = 0, expiration = 0; + + /* + * Entry format: + * <STA addr> <PMKID> <PMK> <expiration in seconds> <akmp> + */ + + if (hwaddr_aton(cmd, spa)) + return -1; + + pos = os_strchr(cmd, ' '); + if (!pos) + return -1; + pos++; + + if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0) + return -1; + + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + + pos2 = os_strchr(pos, ' '); + if (!pos2) + return -1; + pmk_len = (pos2 - pos) / 2; + if (pmk_len < PMK_LEN || pmk_len > PMK_LEN_MAX || + hexstr2bin(pos, pmk, pmk_len) < 0) + return -1; + + pos = pos2 + 1; + + if (sscanf(pos, "%d %d", &expiration, &akmp) != 2) + return -1; + + return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len, + pmkid, expiration, akmp); +} + + +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL +#ifdef CONFIG_MESH + +int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd, + const u8 *addr, char *buf, size_t len) +{ + return wpa_auth_pmksa_list_mesh(hapd->wpa_auth, addr, buf, len); +} + + +void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd) +{ + u8 spa[ETH_ALEN]; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN_MAX]; + char *pos; + int expiration; + + /* + * Entry format: + * <BSSID> <PMKID> <PMK> <expiration in seconds> + */ + + if (hwaddr_aton(cmd, spa)) + return NULL; + + pos = os_strchr(cmd, ' '); + if (!pos) + return NULL; + pos++; + + if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0) + return NULL; + + pos = os_strchr(pos, ' '); + if (!pos) + return NULL; + pos++; + + if (hexstr2bin(pos, pmk, PMK_LEN) < 0) + return NULL; + + pos = os_strchr(pos, ' '); + if (!pos) + return NULL; + pos++; + + if (sscanf(pos, "%d", &expiration) != 1) + return NULL; + + return wpa_auth_pmksa_create_entry(aa, spa, pmk, pmkid, expiration); +} + +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h index 4f996800f132..d1dcebfb957d 100644 --- a/src/ap/ctrl_iface_ap.h +++ b/src/ap/ctrl_iface_ap.h @@ -32,5 +32,9 @@ int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd); int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf, size_t len); void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd); +int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd); +int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd, + const u8 *addr, char *buf, size_t len); +void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd); #endif /* CTRL_IFACE_AP_H */ diff --git a/src/ap/dfs.c b/src/ap/dfs.c index 47adba7ef726..993dd19c2cb0 100644 --- a/src/ap/dfs.c +++ b/src/ap/dfs.c @@ -1,7 +1,7 @@ /* * DFS - Dynamic Frequency Selection * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> - * Copyright (c) 2013-2015, Qualcomm Atheros, Inc. + * Copyright (c) 2013-2017, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -747,6 +747,23 @@ int hostapd_handle_dfs(struct hostapd_iface *iface) } +static int hostapd_config_dfs_chan_available(struct hostapd_iface *iface) +{ + int n_chans, n_chans1, start_chan_idx, start_chan_idx1; + + /* Get the start (first) channel for current configuration */ + start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); + if (start_chan_idx < 0) + return 0; + + /* Get the number of used channels, depending on width */ + n_chans = dfs_get_used_n_chans(iface, &n_chans1); + + /* Check if all channels are DFS available */ + return dfs_check_chans_available(iface, start_chan_idx, n_chans); +} + + int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2) @@ -767,8 +784,21 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, cf1, cf2, HOSTAPD_CHAN_DFS_AVAILABLE); - iface->cac_started = 0; - hostapd_setup_interface_complete(iface, 0); + /* + * Just mark the channel available when CAC completion + * event is received in enabled state. CAC result could + * have been propagated from another radio having the + * same regulatory configuration. When CAC completion is + * received during non-HAPD_IFACE_ENABLED state, make + * sure the configured channel is available because this + * CAC completion event could have been propagated from + * another radio. + */ + if (iface->state != HAPD_IFACE_ENABLED && + hostapd_config_dfs_chan_available(iface)) { + hostapd_setup_interface_complete(iface, 0); + iface->cac_started = 0; + } } } @@ -776,6 +806,25 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, } +int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2) +{ + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_PRE_CAC_EXPIRED + "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", + freq, ht_enabled, chan_offset, chan_width, cf1, cf2); + + /* Proceed only if DFS is not offloaded to the driver */ + if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) + return 0; + + set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, + cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); + + return 0; +} + + static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) { struct hostapd_channel_data *channel; @@ -840,6 +889,13 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) if (iface->cac_started) return hostapd_dfs_start_channel_switch_cac(iface); + /* + * Allow selection of DFS channel in ETSI to comply with + * uniform spreading. + */ + if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI) + skip_radar = 0; + /* Perform channel switch/CSA */ channel = dfs_get_valid_channel(iface, &secondary_channel, &vht_oper_centr_freq_seg0_idx, @@ -1055,7 +1111,8 @@ int hostapd_handle_dfs_offload(struct hostapd_iface *iface) return 1; } - if (ieee80211_is_dfs(iface->freq)) { + if (ieee80211_is_dfs(iface->freq, iface->hw_features, + iface->num_hw_features)) { wpa_printf(MSG_DEBUG, "%s: freq %d MHz requires DFS", __func__, iface->freq); return 0; diff --git a/src/ap/dfs.h b/src/ap/dfs.h index be8c0e6001c9..f0fa6f688037 100644 --- a/src/ap/dfs.h +++ b/src/ap/dfs.h @@ -1,7 +1,7 @@ /* * DFS - Dynamic Frequency Selection * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> - * Copyright (c) 2013, Qualcomm Atheros, Inc. + * Copyright (c) 2013-2017, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,6 +14,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface); int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2); +int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2); int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq, int ht_enabled, int chan_offset, int chan_width, diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c index f0212fb2a984..6d8c2f4be0da 100644 --- a/src/ap/dhcp_snoop.c +++ b/src/ap/dhcp_snoop.c @@ -7,10 +7,9 @@ */ #include "utils/includes.h" -#include <netinet/ip.h> -#include <netinet/udp.h> #include "utils/common.h" +#include "common/dhcp.h" #include "l2_packet/l2_packet.h" #include "hostapd.h" #include "sta_info.h" @@ -18,29 +17,6 @@ #include "x_snoop.h" #include "dhcp_snoop.h" -struct bootp_pkt { - struct iphdr iph; - struct udphdr udph; - u8 op; - u8 htype; - u8 hlen; - u8 hops; - be32 xid; - be16 secs; - be16 flags; - be32 client_ip; - be32 your_ip; - be32 server_ip; - be32 relay_ip; - u8 hw_addr[16]; - u8 serv_name[64]; - u8 boot_file[128]; - u8 exten[312]; -} STRUCT_PACKED; - -#define DHCPACK 5 -static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 }; - static const char * ipaddr_str(u32 addr) { @@ -74,24 +50,26 @@ static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf, if (tot_len > (unsigned int) (len - ETH_HLEN)) return; - if (os_memcmp(b->exten, ic_bootp_cookie, ARRAY_SIZE(ic_bootp_cookie))) + if (WPA_GET_BE32(b->exten) != DHCP_MAGIC) return; /* Parse DHCP options */ end = (const u8 *) b + tot_len; pos = &b->exten[4]; - while (pos < end && *pos != 0xff) { + while (pos < end && *pos != DHCP_OPT_END) { const u8 *opt = pos++; - if (*opt == 0) /* padding */ + if (*opt == DHCP_OPT_PAD) continue; + if (pos >= end || 1 + *pos > end - pos) + break; pos += *pos + 1; if (pos >= end) break; switch (*opt) { - case 1: /* subnet mask */ + case DHCP_OPT_SUBNET_MASK: if (opt[1] == 4) subnet_mask = WPA_GET_BE32(&opt[2]); if (subnet_mask == 0) @@ -101,7 +79,7 @@ static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf, prefixlen--; } break; - case 53: /* message type */ + case DHCP_OPT_MSG_TYPE: if (opt[1]) msgtype = opt[2]; break; @@ -176,4 +154,5 @@ int dhcp_snoop_init(struct hostapd_data *hapd) void dhcp_snoop_deinit(struct hostapd_data *hapd) { l2_packet_deinit(hapd->sock_dhcp); + hapd->sock_dhcp = NULL; } diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c new file mode 100644 index 000000000000..149f389f789f --- /dev/null +++ b/src/ap/dpp_hostapd.c @@ -0,0 +1,2096 @@ +/* + * hostapd / DPP integration + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * 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 "common/dpp.h" +#include "common/gas.h" +#include "common/wpa_ctrl.h" +#include "hostapd.h" +#include "ap_drv_ops.h" +#include "gas_query_ap.h" +#include "wpa_auth.h" +#include "dpp_hostapd.h" + + +static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx); +static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator); +static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx); +static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd); + +static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + +static struct dpp_configurator * +hostapd_dpp_configurator_get_id(struct hostapd_data *hapd, unsigned int id) +{ + struct dpp_configurator *conf; + + dl_list_for_each(conf, &hapd->iface->interfaces->dpp_configurator, + struct dpp_configurator, list) { + if (conf->id == id) + return conf; + } + return NULL; +} + + +static unsigned int hapd_dpp_next_id(struct hostapd_data *hapd) +{ + struct dpp_bootstrap_info *bi; + unsigned int max_id = 0; + + dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap, + struct dpp_bootstrap_info, list) { + if (bi->id > max_id) + max_id = bi->id; + } + return max_id + 1; +} + + +/** + * hostapd_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code + * @hapd: Pointer to hostapd_data + * @cmd: DPP URI read from a QR Code + * Returns: Identifier of the stored info or -1 on failure + */ +int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd) +{ + struct dpp_bootstrap_info *bi; + struct dpp_authentication *auth = hapd->dpp_auth; + + bi = dpp_parse_qr_code(cmd); + if (!bi) + return -1; + + bi->id = hapd_dpp_next_id(hapd); + dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list); + + if (auth && auth->response_pending && + dpp_notify_new_qr_code(auth, bi) == 1) { + wpa_printf(MSG_DEBUG, + "DPP: Sending out pending authentication response"); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", + MAC2STR(auth->peer_mac_addr), auth->curr_freq, + DPP_PA_AUTHENTICATION_RESP); + hostapd_drv_send_action(hapd, auth->curr_freq, 0, + auth->peer_mac_addr, + wpabuf_head(hapd->dpp_auth->resp_msg), + wpabuf_len(hapd->dpp_auth->resp_msg)); + } + + return bi->id; +} + + +static char * get_param(const char *cmd, const char *param) +{ + const char *pos, *end; + char *val; + size_t len; + + pos = os_strstr(cmd, param); + if (!pos) + return NULL; + + pos += os_strlen(param); + end = os_strchr(pos, ' '); + if (end) + len = end - pos; + else + len = os_strlen(pos); + val = os_malloc(len + 1); + if (!val) + return NULL; + os_memcpy(val, pos, len); + val[len] = '\0'; + return val; +} + + +int hostapd_dpp_bootstrap_gen(struct hostapd_data *hapd, const char *cmd) +{ + char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL; + char *key = NULL; + u8 *privkey = NULL; + size_t privkey_len = 0; + size_t len; + int ret = -1; + struct dpp_bootstrap_info *bi; + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + goto fail; + + if (os_strstr(cmd, "type=qrcode")) + bi->type = DPP_BOOTSTRAP_QR_CODE; + else if (os_strstr(cmd, "type=pkex")) + bi->type = DPP_BOOTSTRAP_PKEX; + else + goto fail; + + chan = get_param(cmd, " chan="); + mac = get_param(cmd, " mac="); + info = get_param(cmd, " info="); + curve = get_param(cmd, " curve="); + key = get_param(cmd, " key="); + + if (key) { + privkey_len = os_strlen(key) / 2; + privkey = os_malloc(privkey_len); + if (!privkey || + hexstr2bin(key, privkey, privkey_len) < 0) + goto fail; + } + + pk = dpp_keygen(bi, curve, privkey, privkey_len); + if (!pk) + goto fail; + + len = 4; /* "DPP:" */ + if (chan) { + if (dpp_parse_uri_chan_list(bi, chan) < 0) + goto fail; + len += 3 + os_strlen(chan); /* C:...; */ + } + if (mac) { + if (dpp_parse_uri_mac(bi, mac) < 0) + goto fail; + len += 3 + os_strlen(mac); /* M:...; */ + } + if (info) { + if (dpp_parse_uri_info(bi, info) < 0) + goto fail; + len += 3 + os_strlen(info); /* I:...; */ + } + len += 4 + os_strlen(pk); + bi->uri = os_malloc(len + 1); + if (!bi->uri) + goto fail; + os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;", + chan ? "C:" : "", chan ? chan : "", chan ? ";" : "", + mac ? "M:" : "", mac ? mac : "", mac ? ";" : "", + info ? "I:" : "", info ? info : "", info ? ";" : "", + pk); + bi->id = hapd_dpp_next_id(hapd); + dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list); + ret = bi->id; + bi = NULL; +fail: + os_free(curve); + os_free(pk); + os_free(chan); + os_free(mac); + os_free(info); + str_clear_free(key); + bin_clear_free(privkey, privkey_len); + dpp_bootstrap_info_free(bi); + return ret; +} + + +static struct dpp_bootstrap_info * +dpp_bootstrap_get_id(struct hostapd_data *hapd, unsigned int id) +{ + struct dpp_bootstrap_info *bi; + + dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap, + struct dpp_bootstrap_info, list) { + if (bi->id == id) + return bi; + } + return NULL; +} + + +static int dpp_bootstrap_del(struct hapd_interfaces *ifaces, unsigned int id) +{ + struct dpp_bootstrap_info *bi, *tmp; + int found = 0; + + dl_list_for_each_safe(bi, tmp, &ifaces->dpp_bootstrap, + struct dpp_bootstrap_info, list) { + if (id && bi->id != id) + continue; + found = 1; + dl_list_del(&bi->list); + dpp_bootstrap_info_free(bi); + } + + if (id == 0) + return 0; /* flush succeeds regardless of entries found */ + return found ? 0 : -1; +} + + +int hostapd_dpp_bootstrap_remove(struct hostapd_data *hapd, const char *id) +{ + unsigned int id_val; + + if (os_strcmp(id, "*") == 0) { + id_val = 0; + } else { + id_val = atoi(id); + if (id_val == 0) + return -1; + } + + return dpp_bootstrap_del(hapd->iface->interfaces, id_val); +} + + +const char * hostapd_dpp_bootstrap_get_uri(struct hostapd_data *hapd, + unsigned int id) +{ + struct dpp_bootstrap_info *bi; + + bi = dpp_bootstrap_get_id(hapd, id); + if (!bi) + return NULL; + return bi->uri; +} + + +int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id, + char *reply, int reply_size) +{ + struct dpp_bootstrap_info *bi; + + bi = dpp_bootstrap_get_id(hapd, id); + if (!bi) + return -1; + return os_snprintf(reply, reply_size, "type=%s\n" + "mac_addr=" MACSTR "\n" + "info=%s\n" + "num_freq=%u\n" + "curve=%s\n", + dpp_bootstrap_type_txt(bi->type), + MAC2STR(bi->mac_addr), + bi->info ? bi->info : "", + bi->num_freq, + bi->curve->name); +} + + +static void hostapd_dpp_auth_resp_retry_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct dpp_authentication *auth = hapd->dpp_auth; + + if (!auth || !auth->resp_msg) + return; + + wpa_printf(MSG_DEBUG, + "DPP: Retry Authentication Response after timeout"); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", + MAC2STR(auth->peer_mac_addr), auth->curr_freq, + DPP_PA_AUTHENTICATION_RESP); + hostapd_drv_send_action(hapd, auth->curr_freq, 500, auth->peer_mac_addr, + wpabuf_head(auth->resp_msg), + wpabuf_len(auth->resp_msg)); +} + + +static void hostapd_dpp_auth_resp_retry(struct hostapd_data *hapd) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + unsigned int wait_time, max_tries; + + if (!auth || !auth->resp_msg) + return; + + if (hapd->dpp_resp_max_tries) + max_tries = hapd->dpp_resp_max_tries; + else + max_tries = 5; + auth->auth_resp_tries++; + if (auth->auth_resp_tries >= max_tries) { + wpa_printf(MSG_INFO, + "DPP: No confirm received from initiator - stopping exchange"); + hostapd_drv_send_action_cancel_wait(hapd); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + return; + } + + if (hapd->dpp_resp_retry_time) + wait_time = hapd->dpp_resp_retry_time; + else + wait_time = 1000; + wpa_printf(MSG_DEBUG, + "DPP: Schedule retransmission of Authentication Response frame in %u ms", + wait_time); + eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); + eloop_register_timeout(wait_time / 1000, + (wait_time % 1000) * 1000, + hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); +} + + +void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst, + const u8 *data, size_t data_len, int ok) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + + wpa_printf(MSG_DEBUG, "DPP: TX status: dst=" MACSTR " ok=%d", + MAC2STR(dst), ok); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR + " result=%s", MAC2STR(dst), ok ? "SUCCESS" : "FAILED"); + + if (!hapd->dpp_auth) { + wpa_printf(MSG_DEBUG, + "DPP: Ignore TX status since there is no ongoing authentication exchange"); + return; + } + + if (hapd->dpp_auth->remove_on_tx_status) { + wpa_printf(MSG_DEBUG, + "DPP: Terminate authentication exchange due to an earlier error"); + eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, + hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, + NULL); + hostapd_drv_send_action_cancel_wait(hapd); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + return; + } + + if (hapd->dpp_auth_ok_on_ack) + hostapd_dpp_auth_success(hapd, 1); + + if (!is_broadcast_ether_addr(dst) && !ok) { + wpa_printf(MSG_DEBUG, + "DPP: Unicast DPP Action frame was not ACKed"); + if (auth->waiting_auth_resp) { + /* In case of DPP Authentication Request frame, move to + * the next channel immediately. */ + hostapd_drv_send_action_cancel_wait(hapd); + hostapd_dpp_auth_init_next(hapd); + return; + } + if (auth->waiting_auth_conf) { + hostapd_dpp_auth_resp_retry(hapd); + return; + } + } + + if (!is_broadcast_ether_addr(dst) && auth->waiting_auth_resp && ok) { + /* Allow timeout handling to stop iteration if no response is + * received from a peer that has ACKed a request. */ + auth->auth_req_ack = 1; + } + + if (!hapd->dpp_auth_ok_on_ack && hapd->dpp_auth->neg_freq > 0 && + hapd->dpp_auth->curr_freq != hapd->dpp_auth->neg_freq) { + wpa_printf(MSG_DEBUG, + "DPP: Move from curr_freq %u MHz to neg_freq %u MHz for response", + hapd->dpp_auth->curr_freq, + hapd->dpp_auth->neg_freq); + hostapd_drv_send_action_cancel_wait(hapd); + + if (hapd->dpp_auth->neg_freq != + (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) { + /* TODO: Listen operation on non-operating channel */ + wpa_printf(MSG_INFO, + "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)", + hapd->dpp_auth->neg_freq, hapd->iface->freq); + } + } + + if (hapd->dpp_auth_ok_on_ack) + hapd->dpp_auth_ok_on_ack = 0; +} + + +static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct dpp_authentication *auth = hapd->dpp_auth; + unsigned int freq; + struct os_reltime now, diff; + unsigned int wait_time, diff_ms; + + if (!auth || !auth->waiting_auth_resp) + return; + + wait_time = hapd->dpp_resp_wait_time ? + hapd->dpp_resp_wait_time : 2000; + os_get_reltime(&now); + os_reltime_sub(&now, &hapd->dpp_last_init, &diff); + diff_ms = diff.sec * 1000 + diff.usec / 1000; + wpa_printf(MSG_DEBUG, + "DPP: Reply wait timeout - wait_time=%u diff_ms=%u", + wait_time, diff_ms); + + if (auth->auth_req_ack && diff_ms >= wait_time) { + /* Peer ACK'ed Authentication Request frame, but did not reply + * with Authentication Response frame within two seconds. */ + wpa_printf(MSG_INFO, + "DPP: No response received from responder - stopping initiation attempt"); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_INIT_FAILED); + hostapd_drv_send_action_cancel_wait(hapd); + hostapd_dpp_listen_stop(hapd); + dpp_auth_deinit(auth); + hapd->dpp_auth = NULL; + return; + } + + if (diff_ms >= wait_time) { + /* Authentication Request frame was not ACK'ed and no reply + * was receiving within two seconds. */ + wpa_printf(MSG_DEBUG, + "DPP: Continue Initiator channel iteration"); + hostapd_drv_send_action_cancel_wait(hapd); + hostapd_dpp_listen_stop(hapd); + hostapd_dpp_auth_init_next(hapd); + return; + } + + /* Driver did not support 2000 ms long wait_time with TX command, so + * schedule listen operation to continue waiting for the response. + * + * DPP listen operations continue until stopped, so simply schedule a + * new call to this function at the point when the two second reply + * wait has expired. */ + wait_time -= diff_ms; + + freq = auth->curr_freq; + if (auth->neg_freq > 0) + freq = auth->neg_freq; + wpa_printf(MSG_DEBUG, + "DPP: Continue reply wait on channel %u MHz for %u ms", + freq, wait_time); + hapd->dpp_in_response_listen = 1; + + if (freq != (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) { + /* TODO: Listen operation on non-operating channel */ + wpa_printf(MSG_INFO, + "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)", + freq, hapd->iface->freq); + } + + eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000, + hostapd_dpp_reply_wait_timeout, hapd, NULL); +} + + +static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd, + struct dpp_authentication *auth) +{ +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->dpp_config_obj_override) + auth->config_obj_override = + os_strdup(hapd->dpp_config_obj_override); + if (hapd->dpp_discovery_override) + auth->discovery_override = + os_strdup(hapd->dpp_discovery_override); + if (hapd->dpp_groups_override) + auth->groups_override = os_strdup(hapd->dpp_groups_override); + auth->ignore_netaccesskey_mismatch = + hapd->dpp_ignore_netaccesskey_mismatch; +#endif /* CONFIG_TESTING_OPTIONS */ +} + + +static int hostapd_dpp_set_configurator(struct hostapd_data *hapd, + struct dpp_authentication *auth, + const char *cmd) +{ + const char *pos, *end; + struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL; + struct dpp_configurator *conf = NULL; + u8 ssid[32] = { "test" }; + size_t ssid_len = 4; + char pass[64] = { }; + size_t pass_len = 0; + u8 psk[PMK_LEN]; + int psk_set = 0; + char *group_id = NULL; + + if (!cmd) + return 0; + + wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd); + pos = os_strstr(cmd, " ssid="); + if (pos) { + pos += 6; + end = os_strchr(pos, ' '); + ssid_len = end ? (size_t) (end - pos) : os_strlen(pos); + ssid_len /= 2; + if (ssid_len > sizeof(ssid) || + hexstr2bin(pos, ssid, ssid_len) < 0) + goto fail; + } + + pos = os_strstr(cmd, " pass="); + if (pos) { + pos += 6; + end = os_strchr(pos, ' '); + pass_len = end ? (size_t) (end - pos) : os_strlen(pos); + pass_len /= 2; + if (pass_len > sizeof(pass) - 1 || pass_len < 8 || + hexstr2bin(pos, (u8 *) pass, pass_len) < 0) + goto fail; + } + + pos = os_strstr(cmd, " psk="); + if (pos) { + pos += 5; + if (hexstr2bin(pos, psk, PMK_LEN) < 0) + goto fail; + psk_set = 1; + } + + pos = os_strstr(cmd, " group_id="); + if (pos) { + size_t group_id_len; + + pos += 10; + end = os_strchr(pos, ' '); + group_id_len = end ? (size_t) (end - pos) : os_strlen(pos); + group_id = os_malloc(group_id_len + 1); + if (!group_id) + goto fail; + os_memcpy(group_id, pos, group_id_len); + group_id[group_id_len] = '\0'; + } + + if (os_strstr(cmd, " conf=sta-")) { + conf_sta = os_zalloc(sizeof(struct dpp_configuration)); + if (!conf_sta) + goto fail; + os_memcpy(conf_sta->ssid, ssid, ssid_len); + conf_sta->ssid_len = ssid_len; + if (os_strstr(cmd, " conf=sta-psk") || + os_strstr(cmd, " conf=sta-sae") || + os_strstr(cmd, " conf=sta-psk-sae")) { + if (os_strstr(cmd, " conf=sta-psk-sae")) + conf_sta->akm = DPP_AKM_PSK_SAE; + else if (os_strstr(cmd, " conf=sta-sae")) + conf_sta->akm = DPP_AKM_SAE; + else + conf_sta->akm = DPP_AKM_PSK; + if (psk_set) { + os_memcpy(conf_sta->psk, psk, PMK_LEN); + } else { + conf_sta->passphrase = os_strdup(pass); + if (!conf_sta->passphrase) + goto fail; + } + } else if (os_strstr(cmd, " conf=sta-dpp")) { + conf_sta->akm = DPP_AKM_DPP; + } else { + goto fail; + } + if (os_strstr(cmd, " group_id=")) { + conf_sta->group_id = group_id; + group_id = NULL; + } + } + + if (os_strstr(cmd, " conf=ap-")) { + conf_ap = os_zalloc(sizeof(struct dpp_configuration)); + if (!conf_ap) + goto fail; + os_memcpy(conf_ap->ssid, ssid, ssid_len); + conf_ap->ssid_len = ssid_len; + if (os_strstr(cmd, " conf=ap-psk") || + os_strstr(cmd, " conf=ap-sae") || + os_strstr(cmd, " conf=ap-psk-sae")) { + if (os_strstr(cmd, " conf=ap-psk-sae")) + conf_ap->akm = DPP_AKM_PSK_SAE; + else if (os_strstr(cmd, " conf=ap-sae")) + conf_ap->akm = DPP_AKM_SAE; + else + conf_ap->akm = DPP_AKM_PSK; + if (psk_set) { + os_memcpy(conf_ap->psk, psk, PMK_LEN); + } else if (pass_len > 0) { + conf_ap->passphrase = os_strdup(pass); + if (!conf_ap->passphrase) + goto fail; + } else { + goto fail; + } + } else if (os_strstr(cmd, " conf=ap-dpp")) { + conf_ap->akm = DPP_AKM_DPP; + } else { + goto fail; + } + if (os_strstr(cmd, " group_id=")) { + conf_ap->group_id = group_id; + group_id = NULL; + } + } + + pos = os_strstr(cmd, " expiry="); + if (pos) { + long int val; + + pos += 8; + val = strtol(pos, NULL, 0); + if (val <= 0) + goto fail; + if (conf_sta) + conf_sta->netaccesskey_expiry = val; + if (conf_ap) + conf_ap->netaccesskey_expiry = val; + } + + pos = os_strstr(cmd, " configurator="); + if (pos) { + auth->configurator = 1; + pos += 14; + conf = hostapd_dpp_configurator_get_id(hapd, atoi(pos)); + if (!conf) { + wpa_printf(MSG_INFO, + "DPP: Could not find the specified configurator"); + goto fail; + } + } + auth->conf_sta = conf_sta; + auth->conf_ap = conf_ap; + auth->conf = conf; + os_free(group_id); + return 0; + +fail: + wpa_msg(hapd->msg_ctx, MSG_INFO, + "DPP: Failed to set configurator parameters"); + dpp_configuration_free(conf_sta); + dpp_configuration_free(conf_ap); + os_free(group_id); + return -1; +} + + +static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + + if (!hapd->dpp_auth) + return; + wpa_printf(MSG_DEBUG, "DPP: Retry initiation after timeout"); + hostapd_dpp_auth_init_next(hapd); +} + + +static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + const u8 *dst; + unsigned int wait_time, max_wait_time, freq, max_tries, used; + struct os_reltime now, diff; + + if (!auth) + return -1; + + if (auth->freq_idx == 0) + os_get_reltime(&hapd->dpp_init_iter_start); + + if (auth->freq_idx >= auth->num_freq) { + auth->num_freq_iters++; + if (hapd->dpp_init_max_tries) + max_tries = hapd->dpp_init_max_tries; + else + max_tries = 5; + if (auth->num_freq_iters >= max_tries || auth->auth_req_ack) { + wpa_printf(MSG_INFO, + "DPP: No response received from responder - stopping initiation attempt"); + wpa_msg(hapd->msg_ctx, MSG_INFO, + DPP_EVENT_AUTH_INIT_FAILED); + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, + hapd, NULL); + hostapd_drv_send_action_cancel_wait(hapd); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + return -1; + } + auth->freq_idx = 0; + eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); + if (hapd->dpp_init_retry_time) + wait_time = hapd->dpp_init_retry_time; + else + wait_time = 10000; + os_get_reltime(&now); + os_reltime_sub(&now, &hapd->dpp_init_iter_start, &diff); + used = diff.sec * 1000 + diff.usec / 1000; + if (used > wait_time) + wait_time = 0; + else + wait_time -= used; + wpa_printf(MSG_DEBUG, "DPP: Next init attempt in %u ms", + wait_time); + eloop_register_timeout(wait_time / 1000, + (wait_time % 1000) * 1000, + hostapd_dpp_init_timeout, hapd, + NULL); + return 0; + } + freq = auth->freq[auth->freq_idx++]; + auth->curr_freq = freq; + + if (is_zero_ether_addr(auth->peer_bi->mac_addr)) + dst = broadcast; + else + dst = auth->peer_bi->mac_addr; + hapd->dpp_auth_ok_on_ack = 0; + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); + wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */ + max_wait_time = hapd->dpp_resp_wait_time ? + hapd->dpp_resp_wait_time : 2000; + if (wait_time > max_wait_time) + wait_time = max_wait_time; + wait_time += 10; /* give the driver some extra time to complete */ + eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000, + hostapd_dpp_reply_wait_timeout, hapd, NULL); + wait_time -= 10; + if (auth->neg_freq > 0 && freq != auth->neg_freq) { + wpa_printf(MSG_DEBUG, + "DPP: Initiate on %u MHz and move to neg_freq %u MHz for response", + freq, auth->neg_freq); + } + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", + MAC2STR(dst), freq, DPP_PA_AUTHENTICATION_REQ); + auth->auth_req_ack = 0; + os_get_reltime(&hapd->dpp_last_init); + return hostapd_drv_send_action(hapd, freq, wait_time, + dst, + wpabuf_head(hapd->dpp_auth->req_msg), + wpabuf_len(hapd->dpp_auth->req_msg)); +} + + +int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd) +{ + const char *pos; + struct dpp_bootstrap_info *peer_bi, *own_bi = NULL; + u8 allowed_roles = DPP_CAPAB_CONFIGURATOR; + unsigned int neg_freq = 0; + + pos = os_strstr(cmd, " peer="); + if (!pos) + return -1; + pos += 6; + peer_bi = dpp_bootstrap_get_id(hapd, atoi(pos)); + if (!peer_bi) { + wpa_printf(MSG_INFO, + "DPP: Could not find bootstrapping info for the identified peer"); + return -1; + } + + pos = os_strstr(cmd, " own="); + if (pos) { + pos += 5; + own_bi = dpp_bootstrap_get_id(hapd, atoi(pos)); + if (!own_bi) { + wpa_printf(MSG_INFO, + "DPP: Could not find bootstrapping info for the identified local entry"); + return -1; + } + + if (peer_bi->curve != own_bi->curve) { + wpa_printf(MSG_INFO, + "DPP: Mismatching curves in bootstrapping info (peer=%s own=%s)", + peer_bi->curve->name, own_bi->curve->name); + return -1; + } + } + + pos = os_strstr(cmd, " role="); + if (pos) { + pos += 6; + if (os_strncmp(pos, "configurator", 12) == 0) + allowed_roles = DPP_CAPAB_CONFIGURATOR; + else if (os_strncmp(pos, "enrollee", 8) == 0) + allowed_roles = DPP_CAPAB_ENROLLEE; + else if (os_strncmp(pos, "either", 6) == 0) + allowed_roles = DPP_CAPAB_CONFIGURATOR | + DPP_CAPAB_ENROLLEE; + else + goto fail; + } + + pos = os_strstr(cmd, " neg_freq="); + if (pos) + neg_freq = atoi(pos + 10); + + if (hapd->dpp_auth) { + eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, + hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, + NULL); + hostapd_drv_send_action_cancel_wait(hapd); + dpp_auth_deinit(hapd->dpp_auth); + } + + hapd->dpp_auth = dpp_auth_init(hapd->msg_ctx, peer_bi, own_bi, + allowed_roles, neg_freq, + hapd->iface->hw_features, + hapd->iface->num_hw_features); + if (!hapd->dpp_auth) + goto fail; + hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth); + if (hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, cmd) < 0) { + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + goto fail; + } + + hapd->dpp_auth->neg_freq = neg_freq; + + if (!is_zero_ether_addr(peer_bi->mac_addr)) + os_memcpy(hapd->dpp_auth->peer_mac_addr, peer_bi->mac_addr, + ETH_ALEN); + + return hostapd_dpp_auth_init_next(hapd); +fail: + return -1; +} + + +int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd) +{ + int freq; + + freq = atoi(cmd); + if (freq <= 0) + return -1; + + if (os_strstr(cmd, " role=configurator")) + hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR; + else if (os_strstr(cmd, " role=enrollee")) + hapd->dpp_allowed_roles = DPP_CAPAB_ENROLLEE; + else + hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | + DPP_CAPAB_ENROLLEE; + hapd->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL; + + if (freq != hapd->iface->freq && hapd->iface->freq > 0) { + /* TODO: Listen operation on non-operating channel */ + wpa_printf(MSG_INFO, + "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)", + freq, hapd->iface->freq); + return -1; + } + + return 0; +} + + +void hostapd_dpp_listen_stop(struct hostapd_data *hapd) +{ + /* TODO: Stop listen operation on non-operating channel */ +} + + +static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len, + unsigned int freq) +{ + const u8 *r_bootstrap, *i_bootstrap; + u16 r_bootstrap_len, i_bootstrap_len; + struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL; + + wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR, + MAC2STR(src)); + + r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH, + &r_bootstrap_len); + if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Missing or invalid required Responder Bootstrapping Key Hash attribute"); + return; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash", + r_bootstrap, r_bootstrap_len); + + i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH, + &i_bootstrap_len); + if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) { + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Missing or invalid required Initiator Bootstrapping Key Hash attribute"); + return; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash", + i_bootstrap, i_bootstrap_len); + + /* Try to find own and peer bootstrapping key matches based on the + * received hash values */ + dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap, + struct dpp_bootstrap_info, list) { + if (!own_bi && bi->own && + os_memcmp(bi->pubkey_hash, r_bootstrap, + SHA256_MAC_LEN) == 0) { + wpa_printf(MSG_DEBUG, + "DPP: Found matching own bootstrapping information"); + own_bi = bi; + } + + if (!peer_bi && !bi->own && + os_memcmp(bi->pubkey_hash, i_bootstrap, + SHA256_MAC_LEN) == 0) { + wpa_printf(MSG_DEBUG, + "DPP: Found matching peer bootstrapping information"); + peer_bi = bi; + } + + if (own_bi && peer_bi) + break; + } + + if (!own_bi) { + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "No matching own bootstrapping key found - ignore message"); + return; + } + + if (hapd->dpp_auth) { + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Already in DPP authentication exchange - ignore new one"); + return; + } + + hapd->dpp_auth_ok_on_ack = 0; + hapd->dpp_auth = dpp_auth_req_rx(hapd->msg_ctx, hapd->dpp_allowed_roles, + hapd->dpp_qr_mutual, + peer_bi, own_bi, freq, hdr, buf, len); + if (!hapd->dpp_auth) { + wpa_printf(MSG_DEBUG, "DPP: No response generated"); + return; + } + hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth); + if (hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, + hapd->dpp_configurator_params) < 0) { + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + return; + } + os_memcpy(hapd->dpp_auth->peer_mac_addr, src, ETH_ALEN); + + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", + MAC2STR(src), hapd->dpp_auth->curr_freq, + DPP_PA_AUTHENTICATION_RESP); + hostapd_drv_send_action(hapd, hapd->dpp_auth->curr_freq, 0, + src, wpabuf_head(hapd->dpp_auth->resp_msg), + wpabuf_len(hapd->dpp_auth->resp_msg)); +} + + +static void hostapd_dpp_handle_config_obj(struct hostapd_data *hapd, + struct dpp_authentication *auth) +{ + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_RECEIVED); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_AKM "%s", + dpp_akm_str(auth->akm)); + if (auth->ssid_len) + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s", + wpa_ssid_txt(auth->ssid, auth->ssid_len)); + if (auth->connector) { + /* TODO: Save the Connector and consider using a command + * to fetch the value instead of sending an event with + * it. The Connector could end up being larger than what + * most clients are ready to receive as an event + * message. */ + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONNECTOR "%s", + auth->connector); + } else if (auth->passphrase[0]) { + char hex[64 * 2 + 1]; + + wpa_snprintf_hex(hex, sizeof(hex), + (const u8 *) auth->passphrase, + os_strlen(auth->passphrase)); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PASS "%s", + hex); + } else if (auth->psk_set) { + char hex[PMK_LEN * 2 + 1]; + + wpa_snprintf_hex(hex, sizeof(hex), auth->psk, PMK_LEN); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PSK "%s", + hex); + } + if (auth->c_sign_key) { + char *hex; + size_t hexlen; + + hexlen = 2 * wpabuf_len(auth->c_sign_key) + 1; + hex = os_malloc(hexlen); + if (hex) { + wpa_snprintf_hex(hex, hexlen, + wpabuf_head(auth->c_sign_key), + wpabuf_len(auth->c_sign_key)); + wpa_msg(hapd->msg_ctx, MSG_INFO, + DPP_EVENT_C_SIGN_KEY "%s", hex); + os_free(hex); + } + } + if (auth->net_access_key) { + char *hex; + size_t hexlen; + + hexlen = 2 * wpabuf_len(auth->net_access_key) + 1; + hex = os_malloc(hexlen); + if (hex) { + wpa_snprintf_hex(hex, hexlen, + wpabuf_head(auth->net_access_key), + wpabuf_len(auth->net_access_key)); + if (auth->net_access_key_expiry) + wpa_msg(hapd->msg_ctx, MSG_INFO, + DPP_EVENT_NET_ACCESS_KEY "%s %lu", hex, + (unsigned long) + auth->net_access_key_expiry); + else + wpa_msg(hapd->msg_ctx, MSG_INFO, + DPP_EVENT_NET_ACCESS_KEY "%s", hex); + os_free(hex); + } + } +} + + +static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, + enum gas_query_ap_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code) +{ + struct hostapd_data *hapd = ctx; + const u8 *pos; + struct dpp_authentication *auth = hapd->dpp_auth; + + if (!auth || !auth->auth_success) { + wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); + return; + } + if (!resp || status_code != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "DPP: GAS query did not succeed"); + goto fail; + } + + wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response adv_proto", + adv_proto); + wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response (GAS response)", + resp); + + if (wpabuf_len(adv_proto) != 10 || + !(pos = wpabuf_head(adv_proto)) || + pos[0] != WLAN_EID_ADV_PROTO || + pos[1] != 8 || + pos[3] != WLAN_EID_VENDOR_SPECIFIC || + pos[4] != 5 || + WPA_GET_BE24(&pos[5]) != OUI_WFA || + pos[8] != 0x1a || + pos[9] != 1) { + wpa_printf(MSG_DEBUG, + "DPP: Not a DPP Advertisement Protocol ID"); + goto fail; + } + + if (dpp_conf_resp_rx(auth, resp) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed"); + goto fail; + } + + hostapd_dpp_handle_config_obj(hapd, auth); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + return; + +fail: + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; +} + + +static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + struct wpabuf *buf, *conf_req; + char json[100]; + int res; + int netrole_ap = 1; + + os_snprintf(json, sizeof(json), + "{\"name\":\"Test\"," + "\"wi-fi_tech\":\"infra\"," + "\"netRole\":\"%s\"}", + netrole_ap ? "ap" : "sta"); + wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json); + + conf_req = dpp_build_conf_req(auth, json); + if (!conf_req) { + wpa_printf(MSG_DEBUG, + "DPP: No configuration request data available"); + return; + } + + buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req)); + if (!buf) { + wpabuf_free(conf_req); + return; + } + + /* Advertisement Protocol IE */ + wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(buf, 8); /* Length */ + wpabuf_put_u8(buf, 0x7f); + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 5); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, DPP_OUI_TYPE); + wpabuf_put_u8(buf, 0x01); + + /* GAS Query */ + wpabuf_put_le16(buf, wpabuf_len(conf_req)); + wpabuf_put_buf(buf, conf_req); + wpabuf_free(conf_req); + + wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (freq %u MHz)", + MAC2STR(auth->peer_mac_addr), auth->curr_freq); + + res = gas_query_ap_req(hapd->gas, auth->peer_mac_addr, auth->curr_freq, + buf, hostapd_dpp_gas_resp_cb, hapd); + if (res < 0) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "GAS: Failed to send Query Request"); + wpabuf_free(buf); + } else { + wpa_printf(MSG_DEBUG, + "DPP: GAS query started with dialog token %u", res); + } +} + + +static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator) +{ + wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded"); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=%d", + initiator); +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at Authentication Confirm"); + if (hapd->dpp_auth->configurator) { + /* Prevent GAS response */ + hapd->dpp_auth->auth_success = 0; + } + return; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (!hapd->dpp_auth->configurator) + hostapd_dpp_start_gas_client(hapd); +} + + +static void hostapd_dpp_rx_auth_resp(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len, + unsigned int freq) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "DPP: Authentication Response from " MACSTR, + MAC2STR(src)); + + if (!auth) { + wpa_printf(MSG_DEBUG, + "DPP: No DPP Authentication in progress - drop"); + return; + } + + if (!is_zero_ether_addr(auth->peer_mac_addr) && + os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected " + MACSTR ") - drop", MAC2STR(auth->peer_mac_addr)); + return; + } + + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); + + if (auth->curr_freq != freq && auth->neg_freq == freq) { + wpa_printf(MSG_DEBUG, + "DPP: Responder accepted request for different negotiation channel"); + auth->curr_freq = freq; + } + + eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); + msg = dpp_auth_resp_rx(auth, hdr, buf, len); + if (!msg) { + if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) { + wpa_printf(MSG_DEBUG, "DPP: Wait for full response"); + return; + } + wpa_printf(MSG_DEBUG, "DPP: No confirm generated"); + return; + } + os_memcpy(auth->peer_mac_addr, src, ETH_ALEN); + + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", MAC2STR(src), auth->curr_freq, + DPP_PA_AUTHENTICATION_CONF); + hostapd_drv_send_action(hapd, auth->curr_freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + wpabuf_free(msg); + hapd->dpp_auth_ok_on_ack = 1; +} + + +static void hostapd_dpp_rx_auth_conf(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + + wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation from " MACSTR, + MAC2STR(src)); + + if (!auth) { + wpa_printf(MSG_DEBUG, + "DPP: No DPP Authentication in progress - drop"); + return; + } + + if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected " + MACSTR ") - drop", MAC2STR(auth->peer_mac_addr)); + return; + } + + if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Authentication failed"); + return; + } + + hostapd_dpp_auth_success(hapd, 0); +} + + +static void hostapd_dpp_send_peer_disc_resp(struct hostapd_data *hapd, + const u8 *src, unsigned int freq, + u8 trans_id, + enum dpp_status_error status) +{ + struct wpabuf *msg; + + msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP, + 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector)); + if (!msg) + return; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Transaction ID"); + goto skip_trans_id; + } + if (dpp_test == DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Transaction ID"); + trans_id ^= 0x01; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Transaction ID */ + wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, trans_id); + +#ifdef CONFIG_TESTING_OPTIONS +skip_trans_id: + if (dpp_test == DPP_TEST_NO_STATUS_PEER_DISC_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); + goto skip_status; + } + if (dpp_test == DPP_TEST_INVALID_STATUS_PEER_DISC_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); + status = 254; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* DPP Status */ + wpabuf_put_le16(msg, DPP_ATTR_STATUS); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, status); + +#ifdef CONFIG_TESTING_OPTIONS +skip_status: + if (dpp_test == DPP_TEST_NO_CONNECTOR_PEER_DISC_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Connector"); + goto skip_connector; + } + if (status == DPP_STATUS_OK && + dpp_test == DPP_TEST_INVALID_CONNECTOR_PEER_DISC_RESP) { + char *connector; + + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Connector"); + connector = dpp_corrupt_connector_signature( + hapd->conf->dpp_connector); + if (!connector) { + wpabuf_free(msg); + return; + } + wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR); + wpabuf_put_le16(msg, os_strlen(connector)); + wpabuf_put_str(msg, connector); + os_free(connector); + goto skip_connector; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* DPP Connector */ + if (status == DPP_STATUS_OK) { + wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR); + wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector)); + wpabuf_put_str(msg, hapd->conf->dpp_connector); + } + +#ifdef CONFIG_TESTING_OPTIONS +skip_connector: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_printf(MSG_DEBUG, "DPP: Send Peer Discovery Response to " MACSTR + " status=%d", MAC2STR(src), status); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d status=%d", MAC2STR(src), freq, + DPP_PA_PEER_DISCOVERY_RESP, status); + hostapd_drv_send_action(hapd, freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + wpabuf_free(msg); +} + + +static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd, + const u8 *src, + const u8 *buf, size_t len, + unsigned int freq) +{ + const u8 *connector, *trans_id; + u16 connector_len, trans_id_len; + struct os_time now; + struct dpp_introduction intro; + os_time_t expire; + int expiration; + enum dpp_status_error res; + + wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR, + MAC2STR(src)); + if (!hapd->wpa_auth || + !(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) || + !(hapd->conf->wpa & WPA_PROTO_RSN)) { + wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use"); + return; + } + + if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey || + !hapd->conf->dpp_csign) { + wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set"); + return; + } + + os_get_time(&now); + + if (hapd->conf->dpp_netaccesskey_expiry && + (os_time_t) hapd->conf->dpp_netaccesskey_expiry < now.sec) { + wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired"); + return; + } + + trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID, + &trans_id_len); + if (!trans_id || trans_id_len != 1) { + wpa_printf(MSG_DEBUG, + "DPP: Peer did not include Transaction ID"); + return; + } + + connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len); + if (!connector) { + wpa_printf(MSG_DEBUG, + "DPP: Peer did not include its Connector"); + return; + } + + res = dpp_peer_intro(&intro, hapd->conf->dpp_connector, + wpabuf_head(hapd->conf->dpp_netaccesskey), + wpabuf_len(hapd->conf->dpp_netaccesskey), + wpabuf_head(hapd->conf->dpp_csign), + wpabuf_len(hapd->conf->dpp_csign), + connector, connector_len, &expire); + if (res == 255) { + wpa_printf(MSG_INFO, + "DPP: Network Introduction protocol resulted in internal failure (peer " + MACSTR ")", MAC2STR(src)); + return; + } + if (res != DPP_STATUS_OK) { + wpa_printf(MSG_INFO, + "DPP: Network Introduction protocol resulted in failure (peer " + MACSTR " status %d)", MAC2STR(src), res); + hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0], + res); + return; + } + + if (!expire || (os_time_t) hapd->conf->dpp_netaccesskey_expiry < expire) + expire = hapd->conf->dpp_netaccesskey_expiry; + if (expire) + expiration = expire - now.sec; + else + expiration = 0; + + if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len, + intro.pmkid, expiration, + WPA_KEY_MGMT_DPP) < 0) { + wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry"); + return; + } + + hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0], + DPP_STATUS_OK); +} + + +static void +hostapd_dpp_rx_pkex_exchange_req(struct hostapd_data *hapd, const u8 *src, + const u8 *buf, size_t len, + unsigned int freq) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request from " MACSTR, + MAC2STR(src)); + + /* TODO: Support multiple PKEX codes by iterating over all the enabled + * values here */ + + if (!hapd->dpp_pkex_code || !hapd->dpp_pkex_bi) { + wpa_printf(MSG_DEBUG, + "DPP: No PKEX code configured - ignore request"); + return; + } + + if (hapd->dpp_pkex) { + /* TODO: Support parallel operations */ + wpa_printf(MSG_DEBUG, + "DPP: Already in PKEX session - ignore new request"); + return; + } + + hapd->dpp_pkex = dpp_pkex_rx_exchange_req(hapd->msg_ctx, + hapd->dpp_pkex_bi, + hapd->own_addr, src, + hapd->dpp_pkex_identifier, + hapd->dpp_pkex_code, + buf, len); + if (!hapd->dpp_pkex) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to process the request - ignore it"); + return; + } + + msg = hapd->dpp_pkex->exchange_resp; + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", MAC2STR(src), freq, + DPP_PA_PKEX_EXCHANGE_RESP); + hostapd_drv_send_action(hapd, freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + if (hapd->dpp_pkex->failed) { + wpa_printf(MSG_DEBUG, + "DPP: Terminate PKEX exchange due to an earlier error"); + if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t) + hapd->dpp_pkex->own_bi->pkex_t = hapd->dpp_pkex->t; + dpp_pkex_free(hapd->dpp_pkex); + hapd->dpp_pkex = NULL; + } +} + + +static void +hostapd_dpp_rx_pkex_exchange_resp(struct hostapd_data *hapd, const u8 *src, + const u8 *buf, size_t len, unsigned int freq) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response from " MACSTR, + MAC2STR(src)); + + /* TODO: Support multiple PKEX codes by iterating over all the enabled + * values here */ + + if (!hapd->dpp_pkex || !hapd->dpp_pkex->initiator || + hapd->dpp_pkex->exchange_done) { + wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session"); + return; + } + + msg = dpp_pkex_rx_exchange_resp(hapd->dpp_pkex, src, buf, len); + if (!msg) { + wpa_printf(MSG_DEBUG, "DPP: Failed to process the response"); + return; + } + + wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Request to " MACSTR, + MAC2STR(src)); + + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", MAC2STR(src), freq, + DPP_PA_PKEX_COMMIT_REVEAL_REQ); + hostapd_drv_send_action(hapd, freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + wpabuf_free(msg); +} + + +static void +hostapd_dpp_rx_pkex_commit_reveal_req(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len, + unsigned int freq) +{ + struct wpabuf *msg; + struct dpp_pkex *pkex = hapd->dpp_pkex; + struct dpp_bootstrap_info *bi; + + wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Request from " MACSTR, + MAC2STR(src)); + + if (!pkex || pkex->initiator || !pkex->exchange_done) { + wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session"); + return; + } + + msg = dpp_pkex_rx_commit_reveal_req(pkex, hdr, buf, len); + if (!msg) { + wpa_printf(MSG_DEBUG, "DPP: Failed to process the request"); + if (hapd->dpp_pkex->failed) { + wpa_printf(MSG_DEBUG, "DPP: Terminate PKEX exchange"); + if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t) + hapd->dpp_pkex->own_bi->pkex_t = + hapd->dpp_pkex->t; + dpp_pkex_free(hapd->dpp_pkex); + hapd->dpp_pkex = NULL; + } + return; + } + + wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to " + MACSTR, MAC2STR(src)); + + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", MAC2STR(src), freq, + DPP_PA_PKEX_COMMIT_REVEAL_RESP); + hostapd_drv_send_action(hapd, freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + wpabuf_free(msg); + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + return; + bi->id = hapd_dpp_next_id(hapd); + bi->type = DPP_BOOTSTRAP_PKEX; + os_memcpy(bi->mac_addr, src, ETH_ALEN); + bi->num_freq = 1; + bi->freq[0] = freq; + bi->curve = pkex->own_bi->curve; + bi->pubkey = pkex->peer_bootstrap_key; + pkex->peer_bootstrap_key = NULL; + dpp_pkex_free(pkex); + hapd->dpp_pkex = NULL; + if (dpp_bootstrap_key_hash(bi) < 0) { + dpp_bootstrap_info_free(bi); + return; + } + dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list); +} + + +static void +hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len, + unsigned int freq) +{ + int res; + struct dpp_bootstrap_info *bi, *own_bi; + struct dpp_pkex *pkex = hapd->dpp_pkex; + char cmd[500]; + + wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Response from " MACSTR, + MAC2STR(src)); + + if (!pkex || !pkex->initiator || !pkex->exchange_done) { + wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session"); + return; + } + + res = dpp_pkex_rx_commit_reveal_resp(pkex, hdr, buf, len); + if (res < 0) { + wpa_printf(MSG_DEBUG, "DPP: Failed to process the response"); + return; + } + + own_bi = pkex->own_bi; + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + return; + bi->id = hapd_dpp_next_id(hapd); + bi->type = DPP_BOOTSTRAP_PKEX; + os_memcpy(bi->mac_addr, src, ETH_ALEN); + bi->num_freq = 1; + bi->freq[0] = freq; + bi->curve = own_bi->curve; + bi->pubkey = pkex->peer_bootstrap_key; + pkex->peer_bootstrap_key = NULL; + dpp_pkex_free(pkex); + hapd->dpp_pkex = NULL; + if (dpp_bootstrap_key_hash(bi) < 0) { + dpp_bootstrap_info_free(bi); + return; + } + dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list); + + os_snprintf(cmd, sizeof(cmd), " peer=%u %s", + bi->id, + hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : ""); + wpa_printf(MSG_DEBUG, + "DPP: Start authentication after PKEX with parameters: %s", + cmd); + if (hostapd_dpp_auth_init(hapd, cmd) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Authentication initialization failed"); + return; + } +} + + +void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, + const u8 *buf, size_t len, unsigned int freq) +{ + u8 crypto_suite; + enum dpp_public_action_frame_type type; + const u8 *hdr; + unsigned int pkex_t; + + if (len < DPP_HDR_LEN) + return; + if (WPA_GET_BE24(buf) != OUI_WFA || buf[3] != DPP_OUI_TYPE) + return; + hdr = buf; + buf += 4; + len -= 4; + crypto_suite = *buf++; + type = *buf++; + len -= 2; + + wpa_printf(MSG_DEBUG, + "DPP: Received DPP Public Action frame crypto suite %u type %d from " + MACSTR " freq=%u", + crypto_suite, type, MAC2STR(src), freq); + if (crypto_suite != 1) { + wpa_printf(MSG_DEBUG, "DPP: Unsupported crypto suite %u", + crypto_suite); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR + " freq=%u type=%d ignore=unsupported-crypto-suite", + MAC2STR(src), freq, type); + return; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len); + if (dpp_check_attrs(buf, len) < 0) { + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR + " freq=%u type=%d ignore=invalid-attributes", + MAC2STR(src), freq, type); + return; + } + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR + " freq=%u type=%d", MAC2STR(src), freq, type); + + switch (type) { + case DPP_PA_AUTHENTICATION_REQ: + hostapd_dpp_rx_auth_req(hapd, src, hdr, buf, len, freq); + break; + case DPP_PA_AUTHENTICATION_RESP: + hostapd_dpp_rx_auth_resp(hapd, src, hdr, buf, len, freq); + break; + case DPP_PA_AUTHENTICATION_CONF: + hostapd_dpp_rx_auth_conf(hapd, src, hdr, buf, len); + break; + case DPP_PA_PEER_DISCOVERY_REQ: + hostapd_dpp_rx_peer_disc_req(hapd, src, buf, len, freq); + break; + case DPP_PA_PKEX_EXCHANGE_REQ: + hostapd_dpp_rx_pkex_exchange_req(hapd, src, buf, len, freq); + break; + case DPP_PA_PKEX_EXCHANGE_RESP: + hostapd_dpp_rx_pkex_exchange_resp(hapd, src, buf, len, freq); + break; + case DPP_PA_PKEX_COMMIT_REVEAL_REQ: + hostapd_dpp_rx_pkex_commit_reveal_req(hapd, src, hdr, buf, len, + freq); + break; + case DPP_PA_PKEX_COMMIT_REVEAL_RESP: + hostapd_dpp_rx_pkex_commit_reveal_resp(hapd, src, hdr, buf, len, + freq); + break; + default: + wpa_printf(MSG_DEBUG, + "DPP: Ignored unsupported frame subtype %d", type); + break; + } + + if (hapd->dpp_pkex) + pkex_t = hapd->dpp_pkex->t; + else if (hapd->dpp_pkex_bi) + pkex_t = hapd->dpp_pkex_bi->pkex_t; + else + pkex_t = 0; + if (pkex_t >= PKEX_COUNTER_T_LIMIT) { + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PKEX_T_LIMIT "id=0"); + hostapd_dpp_pkex_remove(hapd, "*"); + } +} + + +struct wpabuf * +hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa, + const u8 *query, size_t query_len) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + struct wpabuf *resp; + + wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa)); + if (!auth || !auth->auth_success || + os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); + return NULL; + } + wpa_hexdump(MSG_DEBUG, + "DPP: Received Configuration Request (GAS Query Request)", + query, query_len); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_REQ_RX "src=" MACSTR, + MAC2STR(sa)); + resp = dpp_conf_req_rx(auth, query, query_len); + if (!resp) + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); + return resp; +} + + +void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok) +{ + if (!hapd->dpp_auth) + return; + + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); + hostapd_drv_send_action_cancel_wait(hapd); + + if (ok) + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT); + else + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; +} + + +static unsigned int hostapd_dpp_next_configurator_id(struct hostapd_data *hapd) +{ + struct dpp_configurator *conf; + unsigned int max_id = 0; + + dl_list_for_each(conf, &hapd->iface->interfaces->dpp_configurator, + struct dpp_configurator, list) { + if (conf->id > max_id) + max_id = conf->id; + } + return max_id + 1; +} + + +int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd) +{ + char *curve = NULL; + char *key = NULL; + u8 *privkey = NULL; + size_t privkey_len = 0; + int ret = -1; + struct dpp_configurator *conf = NULL; + + curve = get_param(cmd, " curve="); + key = get_param(cmd, " key="); + + if (key) { + privkey_len = os_strlen(key) / 2; + privkey = os_malloc(privkey_len); + if (!privkey || + hexstr2bin(key, privkey, privkey_len) < 0) + goto fail; + } + + conf = dpp_keygen_configurator(curve, privkey, privkey_len); + if (!conf) + goto fail; + + conf->id = hostapd_dpp_next_configurator_id(hapd); + dl_list_add(&hapd->iface->interfaces->dpp_configurator, &conf->list); + ret = conf->id; + conf = NULL; +fail: + os_free(curve); + str_clear_free(key); + bin_clear_free(privkey, privkey_len); + dpp_configurator_free(conf); + return ret; +} + + +static int dpp_configurator_del(struct hapd_interfaces *ifaces, unsigned int id) +{ + struct dpp_configurator *conf, *tmp; + int found = 0; + + dl_list_for_each_safe(conf, tmp, &ifaces->dpp_configurator, + struct dpp_configurator, list) { + if (id && conf->id != id) + continue; + found = 1; + dl_list_del(&conf->list); + dpp_configurator_free(conf); + } + + if (id == 0) + return 0; /* flush succeeds regardless of entries found */ + return found ? 0 : -1; +} + + +int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id) +{ + unsigned int id_val; + + if (os_strcmp(id, "*") == 0) { + id_val = 0; + } else { + id_val = atoi(id); + if (id_val == 0) + return -1; + } + + return dpp_configurator_del(hapd->iface->interfaces, id_val); +} + + +int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd) +{ + struct dpp_authentication *auth; + int ret = -1; + char *curve = NULL; + + auth = os_zalloc(sizeof(*auth)); + if (!auth) + return -1; + + curve = get_param(cmd, " curve="); + hostapd_dpp_set_testing_options(hapd, auth); + if (hostapd_dpp_set_configurator(hapd, auth, cmd) == 0 && + dpp_configurator_own_config(auth, curve, 1) == 0) { + hostapd_dpp_handle_config_obj(hapd, auth); + ret = 0; + } + + dpp_auth_deinit(auth); + os_free(curve); + + return ret; +} + + +int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id, + char *buf, size_t buflen) +{ + struct dpp_configurator *conf; + + conf = hostapd_dpp_configurator_get_id(hapd, id); + if (!conf) + return -1; + + return dpp_configurator_get_key(conf, buf, buflen); +} + + +int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd) +{ + struct dpp_bootstrap_info *own_bi; + const char *pos, *end; + + pos = os_strstr(cmd, " own="); + if (!pos) + return -1; + pos += 5; + own_bi = dpp_bootstrap_get_id(hapd, atoi(pos)); + if (!own_bi) { + wpa_printf(MSG_DEBUG, + "DPP: Identified bootstrap info not found"); + return -1; + } + if (own_bi->type != DPP_BOOTSTRAP_PKEX) { + wpa_printf(MSG_DEBUG, + "DPP: Identified bootstrap info not for PKEX"); + return -1; + } + hapd->dpp_pkex_bi = own_bi; + own_bi->pkex_t = 0; /* clear pending errors on new code */ + + os_free(hapd->dpp_pkex_identifier); + hapd->dpp_pkex_identifier = NULL; + pos = os_strstr(cmd, " identifier="); + if (pos) { + pos += 12; + end = os_strchr(pos, ' '); + if (!end) + return -1; + hapd->dpp_pkex_identifier = os_malloc(end - pos + 1); + if (!hapd->dpp_pkex_identifier) + return -1; + os_memcpy(hapd->dpp_pkex_identifier, pos, end - pos); + hapd->dpp_pkex_identifier[end - pos] = '\0'; + } + + pos = os_strstr(cmd, " code="); + if (!pos) + return -1; + os_free(hapd->dpp_pkex_code); + hapd->dpp_pkex_code = os_strdup(pos + 6); + if (!hapd->dpp_pkex_code) + return -1; + + if (os_strstr(cmd, " init=1")) { + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "DPP: Initiating PKEX"); + dpp_pkex_free(hapd->dpp_pkex); + hapd->dpp_pkex = dpp_pkex_init(hapd->msg_ctx, own_bi, + hapd->own_addr, + hapd->dpp_pkex_identifier, + hapd->dpp_pkex_code); + if (!hapd->dpp_pkex) + return -1; + + msg = hapd->dpp_pkex->exchange_req; + /* TODO: Which channel to use? */ + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", MAC2STR(broadcast), 2437, + DPP_PA_PKEX_EXCHANGE_REQ); + hostapd_drv_send_action(hapd, 2437, 0, broadcast, + wpabuf_head(msg), wpabuf_len(msg)); + } + + /* TODO: Support multiple PKEX info entries */ + + os_free(hapd->dpp_pkex_auth_cmd); + hapd->dpp_pkex_auth_cmd = os_strdup(cmd); + + return 1; +} + + +int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id) +{ + unsigned int id_val; + + if (os_strcmp(id, "*") == 0) { + id_val = 0; + } else { + id_val = atoi(id); + if (id_val == 0) + return -1; + } + + if ((id_val != 0 && id_val != 1) || !hapd->dpp_pkex_code) + return -1; + + /* TODO: Support multiple PKEX entries */ + os_free(hapd->dpp_pkex_code); + hapd->dpp_pkex_code = NULL; + os_free(hapd->dpp_pkex_identifier); + hapd->dpp_pkex_identifier = NULL; + os_free(hapd->dpp_pkex_auth_cmd); + hapd->dpp_pkex_auth_cmd = NULL; + hapd->dpp_pkex_bi = NULL; + /* TODO: Remove dpp_pkex only if it is for the identified PKEX code */ + dpp_pkex_free(hapd->dpp_pkex); + hapd->dpp_pkex = NULL; + return 0; +} + + +void hostapd_dpp_stop(struct hostapd_data *hapd) +{ + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + dpp_pkex_free(hapd->dpp_pkex); + hapd->dpp_pkex = NULL; +} + + +int hostapd_dpp_init(struct hostapd_data *hapd) +{ + hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE; + hapd->dpp_init_done = 1; + return 0; +} + + +void hostapd_dpp_deinit(struct hostapd_data *hapd) +{ +#ifdef CONFIG_TESTING_OPTIONS + os_free(hapd->dpp_config_obj_override); + hapd->dpp_config_obj_override = NULL; + os_free(hapd->dpp_discovery_override); + hapd->dpp_discovery_override = NULL; + os_free(hapd->dpp_groups_override); + hapd->dpp_groups_override = NULL; + hapd->dpp_ignore_netaccesskey_mismatch = 0; +#endif /* CONFIG_TESTING_OPTIONS */ + if (!hapd->dpp_init_done) + return; + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + hostapd_dpp_pkex_remove(hapd, "*"); + hapd->dpp_pkex = NULL; + os_free(hapd->dpp_configurator_params); + hapd->dpp_configurator_params = NULL; +} + + +void hostapd_dpp_init_global(struct hapd_interfaces *ifaces) +{ + dl_list_init(&ifaces->dpp_bootstrap); + dl_list_init(&ifaces->dpp_configurator); + ifaces->dpp_init_done = 1; +} + + +void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces) +{ + if (!ifaces->dpp_init_done) + return; + dpp_bootstrap_del(ifaces, 0); + dpp_configurator_del(ifaces, 0); +} diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h new file mode 100644 index 000000000000..3ef7c14567e8 --- /dev/null +++ b/src/ap/dpp_hostapd.h @@ -0,0 +1,43 @@ +/* + * hostapd / DPP integration + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DPP_HOSTAPD_H +#define DPP_HOSTAPD_H + +int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd); +int hostapd_dpp_bootstrap_gen(struct hostapd_data *hapd, const char *cmd); +int hostapd_dpp_bootstrap_remove(struct hostapd_data *hapd, const char *id); +const char * hostapd_dpp_bootstrap_get_uri(struct hostapd_data *hapd, + unsigned int id); +int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id, + char *reply, int reply_size); +int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd); +int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd); +void hostapd_dpp_listen_stop(struct hostapd_data *hapd); +void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, + const u8 *buf, size_t len, unsigned int freq); +void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst, + const u8 *data, size_t data_len, int ok); +struct wpabuf * +hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa, + const u8 *query, size_t query_len); +void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok); +int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd); +int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id); +int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd); +int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id, + char *buf, size_t buflen); +int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd); +int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id); +void hostapd_dpp_stop(struct hostapd_data *hapd); +int hostapd_dpp_init(struct hostapd_data *hapd); +void hostapd_dpp_deinit(struct hostapd_data *hapd); +void hostapd_dpp_init_global(struct hapd_interfaces *ifaces); +void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces); + +#endif /* DPP_HOSTAPD_H */ diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 3552b3e0d53b..a726a6ff87e0 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -31,10 +31,74 @@ #include "wps_hostapd.h" #include "ap_drv_ops.h" #include "ap_config.h" +#include "ap_mlme.h" #include "hw_features.h" #include "dfs.h" #include "beacon.h" #include "mbo_ap.h" +#include "dpp_hostapd.h" +#include "fils_hlp.h" + + +#ifdef CONFIG_FILS +void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd, + struct sta_info *sta) +{ + u16 reply_res = WLAN_STATUS_SUCCESS; + struct ieee802_11_elems elems; + u8 buf[IEEE80211_MAX_MMPDU_SIZE], *p = buf; + int new_assoc; + + wpa_printf(MSG_DEBUG, "%s FILS: Finish association with " MACSTR, + __func__, MAC2STR(sta->addr)); + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); + if (!sta->fils_pending_assoc_req) + return; + + ieee802_11_parse_elems(sta->fils_pending_assoc_req, + sta->fils_pending_assoc_req_len, &elems, 0); + if (!elems.fils_session) { + wpa_printf(MSG_DEBUG, "%s failed to find FILS Session element", + __func__); + return; + } + + p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p, + elems.fils_session, + sta->fils_hlp_resp); + + reply_res = hostapd_sta_assoc(hapd, sta->addr, + sta->fils_pending_assoc_is_reassoc, + WLAN_STATUS_SUCCESS, + buf, p - buf); + ap_sta_set_authorized(hapd, sta, 1); + new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; + hostapd_set_sta_flags(hapd, sta); + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS); + ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + hostapd_new_assoc_sta(hapd, sta, !new_assoc); + os_free(sta->fils_pending_assoc_req); + sta->fils_pending_assoc_req = NULL; + sta->fils_pending_assoc_req_len = 0; + wpabuf_free(sta->fils_hlp_resp); + sta->fils_hlp_resp = NULL; + wpabuf_free(sta->hlp_dhcp_discover); + sta->hlp_dhcp_discover = NULL; + fils_hlp_deinit(hapd); + + /* + * Remove the station in case transmission 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 || sta->added_unassoc) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } +} +#endif /* CONFIG_FILS */ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, @@ -45,10 +109,10 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, struct ieee802_11_elems elems; const u8 *ie; size_t ielen; -#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) +#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) || defined(CONFIG_OWE) u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; u8 *p = buf; -#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ +#endif /* CONFIG_IEEE80211R_AP || CONFIG_IEEE80211W || CONFIG_FILS || CONFIG_OWE */ u16 reason = WLAN_REASON_UNSPECIFIED; u16 status = WLAN_STATUS_SUCCESS; const u8 *p2p_dev_addr = NULL; @@ -171,6 +235,14 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, elems.hs20_len - 4); } else sta->hs20_ie = NULL; + + wpabuf_free(sta->roaming_consortium); + if (elems.roaming_cons_sel) + sta->roaming_consortium = wpabuf_alloc_copy( + elems.roaming_cons_sel + 4, + elems.roaming_cons_sel_len - 4); + else + sta->roaming_consortium = NULL; #endif /* CONFIG_HS20 */ #ifdef CONFIG_FST @@ -198,7 +270,9 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, #endif /* CONFIG_WPS */ wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA"); - return -1; + reason = WLAN_REASON_INVALID_IE; + status = WLAN_STATUS_INVALID_IE; + goto fail; } #ifdef CONFIG_WPS if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 && @@ -231,7 +305,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, ie, ielen, - elems.mdie, elems.mdie_len); + elems.mdie, elems.mdie_len, + elems.owe_dh, elems.owe_dh_len); if (res != WPA_IE_OK) { wpa_printf(MSG_DEBUG, "WPA/RSN information element rejected? (res %u)", @@ -252,8 +327,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, reason = WLAN_REASON_INVALID_IE; status = WLAN_STATUS_INVALID_IE; } else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) { - reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID; - status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; + reason = WLAN_REASON_CIPHER_SUITE_REJECTED; + status = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; } #endif /* CONFIG_IEEE80211W */ else { @@ -263,10 +338,14 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, goto fail; } #ifdef CONFIG_IEEE80211W - if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) == + (WLAN_STA_ASSOC | 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 && + if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) == + (WLAN_STA_ASSOC | WLAN_STA_MFP) && + !sta->sa_query_timed_out && (sta->auth_alg != WLAN_AUTH_FT)) { /* * STA has already been associated with MFP and SA @@ -293,7 +372,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta->flags &= ~WLAN_STA_MFP; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (sta->auth_alg == WLAN_AUTH_FT) { status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies, req_ies_len); @@ -307,7 +386,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, goto fail; } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ } else if (hapd->conf->wps_state) { #ifdef CONFIG_WPS struct wpabuf *wps; @@ -375,19 +454,128 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, skip_wpa_check: #endif /* CONFIG_WPS */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf), sta->auth_alg, req_ies, req_ies_len); + if (!p) { + wpa_printf(MSG_DEBUG, "FT: Failed to write AssocResp IEs"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } +#endif /* CONFIG_IEEE80211R_AP */ + +#ifdef CONFIG_FILS + if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) { + int delay_assoc = 0; + + if (!req_ies) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + if (!wpa_fils_validate_fils_session(sta->wpa_sm, req_ies, + req_ies_len, + sta->fils_session)) { + wpa_printf(MSG_DEBUG, + "FILS: Session validation failed"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + res = wpa_fils_validate_key_confirm(sta->wpa_sm, req_ies, + req_ies_len); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "FILS: Key Confirm validation failed"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (fils_process_hlp(hapd, sta, req_ies, req_ies_len) > 0) { + wpa_printf(MSG_DEBUG, + "FILS: Delaying Assoc Response (HLP)"); + delay_assoc = 1; + } else { + wpa_printf(MSG_DEBUG, + "FILS: Going ahead with Assoc Response (no HLP)"); + } + + if (sta) { + wpa_printf(MSG_DEBUG, "FILS: HLP callback cleanup"); + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); + os_free(sta->fils_pending_assoc_req); + sta->fils_pending_assoc_req = NULL; + sta->fils_pending_assoc_req_len = 0; + wpabuf_free(sta->fils_hlp_resp); + sta->fils_hlp_resp = NULL; + sta->fils_drv_assoc_finish = 0; + } + + if (sta && delay_assoc && status == WLAN_STATUS_SUCCESS) { + u8 *req_tmp; + + req_tmp = os_malloc(req_ies_len); + if (!req_tmp) { + wpa_printf(MSG_DEBUG, + "FILS: buffer allocation failed for assoc req"); + goto fail; + } + os_memcpy(req_tmp, req_ies, req_ies_len); + sta->fils_pending_assoc_req = req_tmp; + sta->fils_pending_assoc_req_len = req_ies_len; + sta->fils_pending_assoc_is_reassoc = reassoc; + sta->fils_drv_assoc_finish = 1; + wpa_printf(MSG_DEBUG, + "FILS: Waiting for HLP processing before sending (Re)Association Response frame to " + MACSTR, MAC2STR(sta->addr)); + eloop_register_timeout( + 0, hapd->conf->fils_hlp_wait_time * 1024, + fils_hlp_timeout, hapd, sta); + return 0; + } + p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p, + elems.fils_session, + sta->fils_hlp_resp); + wpa_hexdump(MSG_DEBUG, "FILS Assoc Resp BUF (IEs)", + buf, p - buf); + } +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE && + elems.owe_dh) { + u8 *npos; + + npos = owe_assoc_req_process(hapd, sta, + elems.owe_dh, elems.owe_dh_len, + p, sizeof(buf) - (p - buf), + &reason); + if (npos) + p = npos; + if (!npos && + reason == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) { + status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + hostapd_sta_assoc(hapd, addr, reassoc, status, buf, + p - buf); + return 0; + } + + if (!npos || reason != WLAN_STATUS_SUCCESS) + goto fail; + } +#endif /* CONFIG_OWE */ + +#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS) || defined(CONFIG_OWE) hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); - if (sta->auth_alg == WLAN_AUTH_FT) + if (sta->auth_alg == WLAN_AUTH_FT || + sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) ap_sta_set_authorized(hapd, sta, 1); -#else /* CONFIG_IEEE80211R */ +#else /* CONFIG_IEEE80211R_AP || CONFIG_FILS */ /* Keep compiler silent about unused variables */ if (status) { } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP || CONFIG_FILS */ new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; @@ -397,6 +585,12 @@ skip_wpa_check: if (reassoc && (sta->auth_alg == WLAN_AUTH_FT)) wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); +#ifdef CONFIG_FILS + else if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS); +#endif /* CONFIG_FILS */ else wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); @@ -414,9 +608,9 @@ skip_wpa_check: return 0; fail: -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ hostapd_drv_sta_disassoc(hapd, sta->addr, reason); ap_free_sta(hapd, sta); return -1; @@ -464,15 +658,81 @@ void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta = ap_get_sta(hapd, addr); - if (!sta || !hapd->conf->disassoc_low_ack) + if (!sta || !hapd->conf->disassoc_low_ack || sta->agreed_to_steer) return; hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disconnected due to excessive missing ACKs"); hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK); - if (sta) - ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK); + ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK); +} + + +void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr, + enum smps_mode smps_mode, + enum chan_width chan_width, u8 rx_nss) +{ + struct sta_info *sta = ap_get_sta(hapd, addr); + const char *txt; + + if (!sta) + return; + + switch (smps_mode) { + case SMPS_AUTOMATIC: + txt = "automatic"; + break; + case SMPS_OFF: + txt = "off"; + break; + case SMPS_DYNAMIC: + txt = "dynamic"; + break; + case SMPS_STATIC: + txt = "static"; + break; + default: + txt = NULL; + break; + } + if (txt) { + wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_SMPS_MODE_CHANGED + MACSTR " %s", MAC2STR(addr), txt); + } + + switch (chan_width) { + case CHAN_WIDTH_20_NOHT: + txt = "20(no-HT)"; + break; + case CHAN_WIDTH_20: + txt = "20"; + break; + case CHAN_WIDTH_40: + txt = "40"; + break; + case CHAN_WIDTH_80: + txt = "80"; + break; + case CHAN_WIDTH_80P80: + txt = "80+80"; + break; + case CHAN_WIDTH_160: + txt = "160"; + break; + default: + txt = NULL; + break; + } + if (txt) { + wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_MAX_BW_CHANGED + MACSTR " %s", MAC2STR(addr), txt); + } + + if (rx_nss != 0xff) { + wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_N_SS_CHANGED + MACSTR " %d", MAC2STR(addr), rx_nss); + } } @@ -485,9 +745,9 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, - "driver had channel switch: freq=%d, ht=%d, offset=%d, width=%d (%s), cf1=%d, cf2=%d", - freq, ht, offset, width, channel_width_to_string(width), - cf1, cf2); + "driver had channel switch: freq=%d, ht=%d, vht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d", + freq, ht, hapd->iconf->ch_switch_vht_config, offset, + width, channel_width_to_string(width), cf1, cf2); hapd->iface->freq = freq; @@ -532,14 +792,26 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, hapd->iconf->channel = channel; hapd->iconf->ieee80211n = ht; - if (!ht) + if (!ht) { hapd->iconf->ieee80211ac = 0; + } else if (hapd->iconf->ch_switch_vht_config) { + /* CHAN_SWITCH VHT config */ + if (hapd->iconf->ch_switch_vht_config & + CH_SWITCH_VHT_ENABLED) + hapd->iconf->ieee80211ac = 1; + else if (hapd->iconf->ch_switch_vht_config & + CH_SWITCH_VHT_DISABLED) + hapd->iconf->ieee80211ac = 0; + } + hapd->iconf->ch_switch_vht_config = 0; + hapd->iconf->secondary_channel = offset; hapd->iconf->vht_oper_chwidth = chwidth; hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx; hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx; - is_dfs = ieee80211_is_dfs(freq); + is_dfs = ieee80211_is_dfs(freq, hapd->iface->hw_features, + hapd->iface->num_hw_features); if (hapd->csa_in_progress && freq == hapd->cs_freq_params.freq) { @@ -690,7 +962,7 @@ int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, #ifdef HOSTAPD -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, u16 auth_transaction, u16 status, @@ -709,7 +981,33 @@ static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst, hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len); } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ + + +#ifdef CONFIG_FILS +static void hostapd_notify_auth_fils_finish(struct hostapd_data *hapd, + struct sta_info *sta, u16 resp, + struct wpabuf *data, int pub) +{ + if (resp == WLAN_STATUS_SUCCESS) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "authentication OK (FILS)"); + sta->flags |= WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->auth_alg = WLAN_AUTH_FILS_SK; + mlme_authenticate_indication(hapd, sta); + } else { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "authentication failed (FILS)"); + } + + hostapd_sta_auth(hapd, sta->addr, 2, resp, + data ? wpabuf_head(data) : NULL, + data ? wpabuf_len(data) : 0); + wpabuf_free(data); +} +#endif /* CONFIG_FILS */ static void hostapd_notif_auth(struct hostapd_data *hapd, @@ -730,7 +1028,7 @@ static void hostapd_notif_auth(struct hostapd_data *hapd, } sta->flags &= ~WLAN_STA_PREAUTH; ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) { sta->auth_alg = WLAN_AUTH_FT; if (sta->wpa_sm == NULL) @@ -748,7 +1046,19 @@ static void hostapd_notif_auth(struct hostapd_data *hapd, hostapd_notify_auth_ft_finish, hapd); return; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ + +#ifdef CONFIG_FILS + if (rx_auth->auth_type == WLAN_AUTH_FILS_SK) { + sta->auth_alg = WLAN_AUTH_FILS_SK; + handle_auth_fils(hapd, sta, rx_auth->ies, rx_auth->ies_len, + rx_auth->auth_type, rx_auth->auth_transaction, + rx_auth->status_code, + hostapd_notify_auth_fils_finish); + return; + } +#endif /* CONFIG_FILS */ + fail: hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1, status, resp_ies, resp_ies_len); @@ -762,32 +1072,36 @@ static void hostapd_action_rx(struct hostapd_data *hapd, struct sta_info *sta; size_t plen __maybe_unused; u16 fc; + u8 *action __maybe_unused; - if (drv_mgmt->frame_len < 24 + 1) + if (drv_mgmt->frame_len < IEEE80211_HDRLEN + 2 + 1) return; - plen = drv_mgmt->frame_len - 24 - 1; + plen = drv_mgmt->frame_len - IEEE80211_HDRLEN - 1; mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame; fc = le_to_host16(mgmt->frame_control); if (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION) return; /* handled by the driver */ - wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d", - mgmt->u.action.category, (int) plen); + action = (u8 *) &mgmt->u.action.u; + wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR + " da " MACSTR " plen %d", + mgmt->u.action.category, *action, + MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) plen); sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { wpa_printf(MSG_DEBUG, "%s: station not found", __func__); return; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (mgmt->u.action.category == WLAN_ACTION_FT) { const u8 *payload = drv_mgmt->frame + 24 + 1; wpa_ft_action_rx(sta->wpa_sm, payload, plen); } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY && plen >= 4) { ieee802_11_sa_query_action( @@ -796,18 +1110,34 @@ static void hostapd_action_rx(struct hostapd_data *hapd, mgmt->u.action.u.sa_query_resp.trans_id); } #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP if (mgmt->u.action.category == WLAN_ACTION_WNM) { ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len); } -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ #ifdef CONFIG_FST if (mgmt->u.action.category == WLAN_ACTION_FST && hapd->iface->fst) { fst_rx_action(hapd->iface->fst, mgmt, drv_mgmt->frame_len); return; } #endif /* CONFIG_FST */ +#ifdef CONFIG_DPP + if (plen >= 1 + 4 && + mgmt->u.action.u.vs_public_action.action == + WLAN_PA_VENDOR_SPECIFIC && + WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) == + OUI_WFA && + mgmt->u.action.u.vs_public_action.variable[0] == + DPP_OUI_TYPE) { + const u8 *pos, *end; + pos = mgmt->u.action.u.vs_public_action.oui; + end = drv_mgmt->frame + drv_mgmt->frame_len; + hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos, + drv_mgmt->freq); + return; + } +#endif /* CONFIG_DPP */ } @@ -891,6 +1221,7 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) } os_memset(&fi, 0, sizeof(fi)); + fi.freq = rx_mgmt->freq; fi.datarate = rx_mgmt->datarate; fi.ssi_signal = rx_mgmt->ssi_signal; @@ -1122,6 +1453,16 @@ static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd, } +static void hostapd_event_dfs_pre_cac_expired(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS Pre-CAC expired on %d MHz", radar->freq); + hostapd_dfs_pre_cac_expired(hapd->iface, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + + static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd, struct dfs_event *radar) { @@ -1164,6 +1505,28 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd, #endif /* NEED_AP_MLME */ +static void hostapd_event_wds_sta_interface_status(struct hostapd_data *hapd, + int istatus, + const char *ifname, + const u8 *addr) +{ + struct sta_info *sta = ap_get_sta(hapd, addr); + + if (sta) { + os_free(sta->ifname_wds); + if (istatus == INTERFACE_ADDED) + sta->ifname_wds = os_strdup(ifname); + else + sta->ifname_wds = NULL; + } + + wpa_msg(hapd->msg_ctx, MSG_INFO, "%sifname=%s sta_addr=" MACSTR, + istatus == INTERFACE_ADDED ? + WDS_STA_INTERFACE_ADDED : WDS_STA_INTERFACE_REMOVED, + ifname, MAC2STR(addr)); +} + + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { @@ -1314,6 +1677,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; hostapd_event_dfs_radar_detected(hapd, &data->dfs_event); break; + case EVENT_DFS_PRE_CAC_EXPIRED: + if (!data) + break; + hostapd_event_dfs_pre_cac_expired(hapd, &data->dfs_event); + break; case EVENT_DFS_CAC_FINISHED: if (!data) break; @@ -1351,7 +1719,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, * Try to re-enable interface if the driver stopped it * when the interface got disabled. */ - wpa_auth_reconfig_group_keys(hapd->wpa_auth); + if (hapd->wpa_auth) + wpa_auth_reconfig_group_keys(hapd->wpa_auth); + else + hostapd_reconfig_encryption(hapd); hapd->reenable_beacon = 1; ieee802_11_set_beacon(hapd); } @@ -1367,6 +1738,18 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, &data->acs_selected_channels); break; #endif /* CONFIG_ACS */ + case EVENT_STATION_OPMODE_CHANGED: + hostapd_event_sta_opmode_changed(hapd, data->sta_opmode.addr, + data->sta_opmode.smps_mode, + data->sta_opmode.chan_width, + data->sta_opmode.rx_nss); + break; + case EVENT_WDS_STA_INTERFACE_STATUS: + hostapd_event_wds_sta_interface_status( + hapd, data->wds_sta_interface.istatus, + data->wds_sta_interface.ifname, + data->wds_sta_interface.sta_addr); + break; default: wpa_printf(MSG_DEBUG, "Unknown event %d", event); break; diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c index 082d0f53175e..296d5c2ddf31 100644 --- a/src/ap/eap_user_db.c +++ b/src/ap/eap_user_db.c @@ -91,6 +91,8 @@ static int get_user_cb(void *ctx, int argc, char *argv[], char *col[]) set_user_methods(user, argv[i]); } else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) { user->remediation = strlen(argv[i]) > 0; + } else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) { + user->t_c_timestamp = strtol(argv[i], NULL, 10); } } diff --git a/src/ap/eth_p_oui.c b/src/ap/eth_p_oui.c new file mode 100644 index 000000000000..aba901e3fa75 --- /dev/null +++ b/src/ap/eth_p_oui.c @@ -0,0 +1,191 @@ +/* + * hostapd / IEEE 802 OUI Extended EtherType 88-B7 + * Copyright (c) 2016, Jouni Malinen <j@w1.fi> + * + * 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 "l2_packet/l2_packet.h" +#include "hostapd.h" +#include "eth_p_oui.h" + +/* + * See IEEE Std 802-2014, Clause 9.2.4 for the definition of the OUI Extended + * EtherType 88-B7. This file implements this with OUI 00:13:74 and + * vendor-specific subtype 0x0001. + */ +static const u8 global_oui[] = { 0x00, 0x13, 0x74, 0x00, 0x01 }; + +struct eth_p_oui_iface { + struct dl_list list; + char ifname[IFNAMSIZ + 1]; + struct l2_packet_data *l2; + struct dl_list receiver; +}; + +struct eth_p_oui_ctx { + struct dl_list list; + struct eth_p_oui_iface *iface; + /* all data needed to deliver and unregister */ + u8 oui_suffix; /* last byte of OUI */ + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, + const u8 *buf, size_t len); + void *rx_callback_ctx; +}; + + +void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr, + const u8 *dst_addr, const u8 *buf, size_t len) +{ + ctx->rx_callback(ctx->rx_callback_ctx, src_addr, dst_addr, + ctx->oui_suffix, buf, len); +} + + +static void eth_p_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct eth_p_oui_iface *iface = ctx; + struct eth_p_oui_ctx *receiver; + const struct l2_ethhdr *ethhdr; + + if (len < sizeof(*ethhdr) + sizeof(global_oui) + 1) { + /* too short packet */ + return; + } + + ethhdr = (struct l2_ethhdr *) buf; + /* trim eth_hdr from buf and len */ + buf += sizeof(*ethhdr); + len -= sizeof(*ethhdr); + + /* verify OUI and vendor-specific subtype match */ + if (os_memcmp(buf, global_oui, sizeof(global_oui)) != 0) + return; + buf += sizeof(global_oui); + len -= sizeof(global_oui); + + dl_list_for_each(receiver, &iface->receiver, + struct eth_p_oui_ctx, list) { + if (buf[0] != receiver->oui_suffix) + continue; + + eth_p_oui_deliver(receiver, ethhdr->h_source, ethhdr->h_dest, + buf + 1, len - 1); + } +} + + +struct eth_p_oui_ctx * +eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, + const u8 *buf, size_t len), + void *rx_callback_ctx) +{ + struct eth_p_oui_iface *iface; + struct eth_p_oui_ctx *receiver; + int found = 0; + struct hapd_interfaces *interfaces; + + receiver = os_zalloc(sizeof(*receiver)); + if (!receiver) + goto err; + + receiver->oui_suffix = oui_suffix; + receiver->rx_callback = rx_callback; + receiver->rx_callback_ctx = rx_callback_ctx; + + interfaces = hapd->iface->interfaces; + + dl_list_for_each(iface, &interfaces->eth_p_oui, struct eth_p_oui_iface, + list) { + if (os_strcmp(iface->ifname, ifname) != 0) + continue; + found = 1; + break; + } + + if (!found) { + iface = os_zalloc(sizeof(*iface)); + if (!iface) + goto err; + + os_strlcpy(iface->ifname, ifname, sizeof(iface->ifname)); + iface->l2 = l2_packet_init(ifname, NULL, ETH_P_OUI, eth_p_rx, + iface, 1); + if (!iface->l2) { + os_free(iface); + goto err; + } + dl_list_init(&iface->receiver); + + dl_list_add_tail(&interfaces->eth_p_oui, &iface->list); + } + + dl_list_add_tail(&iface->receiver, &receiver->list); + receiver->iface = iface; + + return receiver; +err: + os_free(receiver); + return NULL; +} + + +void eth_p_oui_unregister(struct eth_p_oui_ctx *ctx) +{ + struct eth_p_oui_iface *iface; + + if (!ctx) + return; + + iface = ctx->iface; + + dl_list_del(&ctx->list); + os_free(ctx); + + if (dl_list_empty(&iface->receiver)) { + dl_list_del(&iface->list); + l2_packet_deinit(iface->l2); + os_free(iface); + } +} + + +int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr, + const u8 *dst_addr, const u8 *buf, size_t len) +{ + struct eth_p_oui_iface *iface = ctx->iface; + u8 *packet, *p; + size_t packet_len; + int ret; + struct l2_ethhdr *ethhdr; + + packet_len = sizeof(*ethhdr) + sizeof(global_oui) + 1 + len; + packet = os_zalloc(packet_len); + if (!packet) + return -1; + p = packet; + + ethhdr = (struct l2_ethhdr *) packet; + os_memcpy(ethhdr->h_source, src_addr, ETH_ALEN); + os_memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN); + ethhdr->h_proto = host_to_be16(ETH_P_OUI); + p += sizeof(*ethhdr); + + os_memcpy(p, global_oui, sizeof(global_oui)); + p[sizeof(global_oui)] = ctx->oui_suffix; + p += sizeof(global_oui) + 1; + + os_memcpy(p, buf, len); + + ret = l2_packet_send(iface->l2, NULL, 0, packet, packet_len); + os_free(packet); + return ret; +} diff --git a/src/ap/eth_p_oui.h b/src/ap/eth_p_oui.h new file mode 100644 index 000000000000..466fdc39c6f8 --- /dev/null +++ b/src/ap/eth_p_oui.h @@ -0,0 +1,28 @@ +/* + * hostapd / IEEE 802 OUI Extended Ethertype + * Copyright (c) 2016, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef ETH_P_OUI_H +#define ETH_P_OUI_H + +struct eth_p_oui_ctx; +struct hostapd_data; + +/* rx_callback only gets payload after OUI passed as buf */ +struct eth_p_oui_ctx * +eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, + const u8 *buf, size_t len), + void *rx_callback_ctx); +void eth_p_oui_unregister(struct eth_p_oui_ctx *eth_p_oui); +int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr, + const u8 *dst_addr, const u8 *buf, size_t len); +void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr, + const u8 *dst_addr, const u8 *buf, size_t len); + +#endif /* ETH_P_OUI_H */ diff --git a/src/ap/fils_hlp.c b/src/ap/fils_hlp.c new file mode 100644 index 000000000000..2a359ab03c81 --- /dev/null +++ b/src/ap/fils_hlp.c @@ -0,0 +1,641 @@ +/* + * FILS HLP request processing + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * 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 "common/dhcp.h" +#include "hostapd.h" +#include "sta_info.h" +#include "ieee802_11.h" +#include "fils_hlp.h" + + +static be16 ip_checksum(const void *buf, size_t len) +{ + u32 sum = 0; + const u16 *pos; + + for (pos = buf; len >= 2; len -= 2) + sum += ntohs(*pos++); + if (len) + sum += ntohs(*pos << 8); + + sum = (sum >> 16) + (sum & 0xffff); + sum += sum >> 16; + return htons(~sum); +} + + +static int fils_dhcp_request(struct hostapd_data *hapd, struct sta_info *sta, + struct dhcp_data *dhcpoffer, u8 *dhcpofferend) +{ + u8 *pos, *end; + struct dhcp_data *dhcp; + struct sockaddr_in addr; + ssize_t res; + const u8 *server_id = NULL; + + if (!sta->hlp_dhcp_discover) { + wpa_printf(MSG_DEBUG, + "FILS: No pending HLP DHCPDISCOVER available"); + return -1; + } + + /* Convert to DHCPREQUEST, remove rapid commit option, replace requested + * IP address option with yiaddr. */ + pos = wpabuf_mhead(sta->hlp_dhcp_discover); + end = pos + wpabuf_len(sta->hlp_dhcp_discover); + dhcp = (struct dhcp_data *) pos; + pos = (u8 *) (dhcp + 1); + pos += 4; /* skip magic */ + while (pos < end && *pos != DHCP_OPT_END) { + u8 opt, olen; + + opt = *pos++; + if (opt == DHCP_OPT_PAD) + continue; + if (pos >= end) + break; + olen = *pos++; + if (olen > end - pos) + break; + + switch (opt) { + case DHCP_OPT_MSG_TYPE: + if (olen > 0) + *pos = DHCPREQUEST; + break; + case DHCP_OPT_RAPID_COMMIT: + case DHCP_OPT_REQUESTED_IP_ADDRESS: + case DHCP_OPT_SERVER_ID: + /* Remove option */ + pos -= 2; + os_memmove(pos, pos + 2 + olen, end - pos - 2 - olen); + end -= 2 + olen; + olen = 0; + break; + } + pos += olen; + } + if (pos >= end || *pos != DHCP_OPT_END) { + wpa_printf(MSG_DEBUG, "FILS: Could not update DHCPDISCOVER"); + return -1; + } + sta->hlp_dhcp_discover->used = pos - (u8 *) dhcp; + + /* Copy Server ID option from DHCPOFFER to DHCPREQUEST */ + pos = (u8 *) (dhcpoffer + 1); + end = dhcpofferend; + pos += 4; /* skip magic */ + while (pos < end && *pos != DHCP_OPT_END) { + u8 opt, olen; + + opt = *pos++; + if (opt == DHCP_OPT_PAD) + continue; + if (pos >= end) + break; + olen = *pos++; + if (olen > end - pos) + break; + + switch (opt) { + case DHCP_OPT_SERVER_ID: + server_id = pos - 2; + break; + } + pos += olen; + } + + if (wpabuf_resize(&sta->hlp_dhcp_discover, + 6 + 1 + (server_id ? 2 + server_id[1] : 0))) + return -1; + if (server_id) + wpabuf_put_data(sta->hlp_dhcp_discover, server_id, + 2 + server_id[1]); + wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_REQUESTED_IP_ADDRESS); + wpabuf_put_u8(sta->hlp_dhcp_discover, 4); + wpabuf_put_data(sta->hlp_dhcp_discover, &dhcpoffer->your_ip, 4); + wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_END); + + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr; + addr.sin_port = htons(hapd->conf->dhcp_server_port); + res = sendto(hapd->dhcp_sock, wpabuf_head(sta->hlp_dhcp_discover), + wpabuf_len(sta->hlp_dhcp_discover), 0, + (const struct sockaddr *) &addr, sizeof(addr)); + if (res < 0) { + wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s", + strerror(errno)); + return -1; + } + wpa_printf(MSG_DEBUG, + "FILS: Acting as DHCP rapid commit proxy for %s:%d", + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + wpabuf_free(sta->hlp_dhcp_discover); + sta->hlp_dhcp_discover = NULL; + sta->fils_dhcp_rapid_commit_proxy = 1; + return 0; +} + + +static void fils_dhcp_handler(int sd, void *eloop_ctx, void *sock_ctx) +{ + struct hostapd_data *hapd = sock_ctx; + struct sta_info *sta; + u8 buf[1500], *pos, *end, *end_opt = NULL; + struct dhcp_data *dhcp; + struct sockaddr_in addr; + socklen_t addr_len; + ssize_t res; + u8 msgtype = 0; + int rapid_commit = 0; + struct iphdr *iph; + struct udphdr *udph; + struct wpabuf *resp; + const u8 *rpos; + size_t left, len; + + addr_len = sizeof(addr); + res = recvfrom(sd, buf, sizeof(buf), 0, + (struct sockaddr *) &addr, &addr_len); + if (res < 0) { + wpa_printf(MSG_DEBUG, "FILS: DHCP read failed: %s", + strerror(errno)); + return; + } + wpa_printf(MSG_DEBUG, "FILS: DHCP response from server %s:%d (len=%d)", + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), (int) res); + wpa_hexdump(MSG_MSGDUMP, "FILS: HLP - DHCP server response", buf, res); + if ((size_t) res < sizeof(*dhcp)) + return; + dhcp = (struct dhcp_data *) buf; + if (dhcp->op != 2) + return; /* Not a BOOTREPLY */ + if (dhcp->relay_ip != hapd->conf->own_ip_addr.u.v4.s_addr) { + wpa_printf(MSG_DEBUG, + "FILS: HLP - DHCP response to unknown relay address 0x%x", + dhcp->relay_ip); + return; + } + dhcp->relay_ip = 0; + pos = (u8 *) (dhcp + 1); + end = &buf[res]; + + if (end - pos < 4 || WPA_GET_BE32(pos) != DHCP_MAGIC) { + wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic in response"); + return; + } + pos += 4; + + wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options in response", + pos, end - pos); + while (pos < end && *pos != DHCP_OPT_END) { + u8 opt, olen; + + opt = *pos++; + if (opt == DHCP_OPT_PAD) + continue; + if (pos >= end) + break; + olen = *pos++; + if (olen > end - pos) + break; + + switch (opt) { + case DHCP_OPT_MSG_TYPE: + if (olen > 0) + msgtype = pos[0]; + break; + case DHCP_OPT_RAPID_COMMIT: + rapid_commit = 1; + break; + } + pos += olen; + } + if (pos < end && *pos == DHCP_OPT_END) + end_opt = pos; + + wpa_printf(MSG_DEBUG, + "FILS: HLP - DHCP message type %u (rapid_commit=%d hw_addr=" + MACSTR ")", + msgtype, rapid_commit, MAC2STR(dhcp->hw_addr)); + + sta = ap_get_sta(hapd, dhcp->hw_addr); + if (!sta || !sta->fils_pending_assoc_req) { + wpa_printf(MSG_DEBUG, + "FILS: No pending HLP DHCP exchange with hw_addr " + MACSTR, MAC2STR(dhcp->hw_addr)); + return; + } + + if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPOFFER && + !rapid_commit) { + /* Use hostapd to take care of 4-message exchange and convert + * the final DHCPACK to rapid commit version. */ + if (fils_dhcp_request(hapd, sta, dhcp, end) == 0) + return; + /* failed, so send the server response as-is */ + } else if (msgtype != DHCPACK) { + wpa_printf(MSG_DEBUG, + "FILS: No DHCPACK available from the server and cannot do rapid commit proxying"); + } + + pos = buf; + resp = wpabuf_alloc(2 * ETH_ALEN + 6 + 2 + + sizeof(*iph) + sizeof(*udph) + (end - pos) + 2); + if (!resp) + return; + wpabuf_put_data(resp, sta->addr, ETH_ALEN); + wpabuf_put_data(resp, hapd->own_addr, ETH_ALEN); + wpabuf_put_data(resp, "\xaa\xaa\x03\x00\x00\x00", 6); + wpabuf_put_be16(resp, ETH_P_IP); + iph = wpabuf_put(resp, sizeof(*iph)); + iph->version = 4; + iph->ihl = sizeof(*iph) / 4; + iph->tot_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos)); + iph->ttl = 1; + iph->protocol = 17; /* UDP */ + iph->saddr = hapd->conf->dhcp_server.u.v4.s_addr; + iph->daddr = dhcp->client_ip; + iph->check = ip_checksum(iph, sizeof(*iph)); + udph = wpabuf_put(resp, sizeof(*udph)); + udph->uh_sport = htons(DHCP_SERVER_PORT); + udph->uh_dport = htons(DHCP_CLIENT_PORT); + udph->uh_ulen = htons(sizeof(*udph) + (end - pos)); + udph->uh_sum = htons(0x0000); /* TODO: calculate checksum */ + if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPACK && + !rapid_commit && sta->fils_dhcp_rapid_commit_proxy && end_opt) { + /* Add rapid commit option */ + wpabuf_put_data(resp, pos, end_opt - pos); + wpabuf_put_u8(resp, DHCP_OPT_RAPID_COMMIT); + wpabuf_put_u8(resp, 0); + wpabuf_put_data(resp, end_opt, end - end_opt); + } else { + wpabuf_put_data(resp, pos, end - pos); + } + if (wpabuf_resize(&sta->fils_hlp_resp, wpabuf_len(resp) + + 2 * wpabuf_len(resp) / 255 + 100)) { + wpabuf_free(resp); + return; + } + + rpos = wpabuf_head(resp); + left = wpabuf_len(resp); + + wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXTENSION); /* Element ID */ + if (left <= 254) + len = 1 + left; + else + len = 255; + wpabuf_put_u8(sta->fils_hlp_resp, len); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXT_FILS_HLP_CONTAINER); + /* Destination MAC Address, Source MAC Address, HLP Packet. + * HLP Packet is in MSDU format (i.e., including the LLC/SNAP header + * when LPD is used). */ + wpabuf_put_data(sta->fils_hlp_resp, rpos, len - 1); + rpos += len - 1; + left -= len - 1; + while (left) { + wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_FRAGMENT); + len = left > 255 ? 255 : left; + wpabuf_put_u8(sta->fils_hlp_resp, len); + wpabuf_put_data(sta->fils_hlp_resp, rpos, len); + rpos += len; + left -= len; + } + wpabuf_free(resp); + + if (sta->fils_drv_assoc_finish) + hostapd_notify_assoc_fils_finish(hapd, sta); + else + fils_hlp_finish_assoc(hapd, sta); +} + + +static int fils_process_hlp_dhcp(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *msg, size_t len) +{ + const struct dhcp_data *dhcp; + struct wpabuf *dhcp_buf; + struct dhcp_data *dhcp_msg; + u8 msgtype = 0; + int rapid_commit = 0; + const u8 *pos = msg, *end; + struct sockaddr_in addr; + ssize_t res; + + if (len < sizeof(*dhcp)) + return 0; + dhcp = (const struct dhcp_data *) pos; + end = pos + len; + wpa_printf(MSG_DEBUG, + "FILS: HLP request DHCP: op=%u htype=%u hlen=%u hops=%u xid=0x%x", + dhcp->op, dhcp->htype, dhcp->hlen, dhcp->hops, + ntohl(dhcp->xid)); + pos += sizeof(*dhcp); + if (dhcp->op != 1) + return 0; /* Not a BOOTREQUEST */ + + if (end - pos < 4) + return 0; + if (WPA_GET_BE32(pos) != DHCP_MAGIC) { + wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic"); + return 0; + } + pos += 4; + + wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options", pos, end - pos); + while (pos < end && *pos != DHCP_OPT_END) { + u8 opt, olen; + + opt = *pos++; + if (opt == DHCP_OPT_PAD) + continue; + if (pos >= end) + break; + olen = *pos++; + if (olen > end - pos) + break; + + switch (opt) { + case DHCP_OPT_MSG_TYPE: + if (olen > 0) + msgtype = pos[0]; + break; + case DHCP_OPT_RAPID_COMMIT: + rapid_commit = 1; + break; + } + pos += olen; + } + + wpa_printf(MSG_DEBUG, "FILS: HLP - DHCP message type %u", msgtype); + if (msgtype != DHCPDISCOVER) + return 0; + + if (hapd->conf->dhcp_server.af != AF_INET || + hapd->conf->dhcp_server.u.v4.s_addr == 0) { + wpa_printf(MSG_DEBUG, + "FILS: HLP - no DHCPv4 server configured - drop request"); + return 0; + } + + if (hapd->conf->own_ip_addr.af != AF_INET || + hapd->conf->own_ip_addr.u.v4.s_addr == 0) { + wpa_printf(MSG_DEBUG, + "FILS: HLP - no IPv4 own_ip_addr configured - drop request"); + return 0; + } + + if (hapd->dhcp_sock < 0) { + int s; + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, + "FILS: Failed to open DHCP socket: %s", + strerror(errno)); + return 0; + } + + if (hapd->conf->dhcp_relay_port) { + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = + hapd->conf->own_ip_addr.u.v4.s_addr; + addr.sin_port = htons(hapd->conf->dhcp_relay_port); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr))) { + wpa_printf(MSG_ERROR, + "FILS: Failed to bind DHCP socket: %s", + strerror(errno)); + close(s); + return 0; + } + } + if (eloop_register_sock(s, EVENT_TYPE_READ, + fils_dhcp_handler, NULL, hapd)) { + close(s); + return 0; + } + + hapd->dhcp_sock = s; + } + + dhcp_buf = wpabuf_alloc(len); + if (!dhcp_buf) + return 0; + dhcp_msg = wpabuf_put(dhcp_buf, len); + os_memcpy(dhcp_msg, msg, len); + dhcp_msg->relay_ip = hapd->conf->own_ip_addr.u.v4.s_addr; + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr; + addr.sin_port = htons(hapd->conf->dhcp_server_port); + res = sendto(hapd->dhcp_sock, dhcp_msg, len, 0, + (const struct sockaddr *) &addr, sizeof(addr)); + if (res < 0) { + wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s", + strerror(errno)); + wpabuf_free(dhcp_buf); + /* Close the socket to try to recover from error */ + eloop_unregister_read_sock(hapd->dhcp_sock); + close(hapd->dhcp_sock); + hapd->dhcp_sock = -1; + return 0; + } + + wpa_printf(MSG_DEBUG, + "FILS: HLP relayed DHCP request to server %s:%d (rapid_commit=%d)", + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), + rapid_commit); + if (hapd->conf->dhcp_rapid_commit_proxy && rapid_commit) { + /* Store a copy of the DHCPDISCOVER for rapid commit proxying + * purposes if the server does not support the rapid commit + * option. */ + wpa_printf(MSG_DEBUG, + "FILS: Store DHCPDISCOVER for rapid commit proxy"); + wpabuf_free(sta->hlp_dhcp_discover); + sta->hlp_dhcp_discover = dhcp_buf; + } else { + wpabuf_free(dhcp_buf); + } + + return 1; +} + + +static int fils_process_hlp_udp(struct hostapd_data *hapd, + struct sta_info *sta, const u8 *dst, + const u8 *pos, size_t len) +{ + const struct iphdr *iph; + const struct udphdr *udph; + u16 sport, dport, ulen; + + if (len < sizeof(*iph) + sizeof(*udph)) + return 0; + iph = (const struct iphdr *) pos; + udph = (const struct udphdr *) (iph + 1); + sport = ntohs(udph->uh_sport); + dport = ntohs(udph->uh_dport); + ulen = ntohs(udph->uh_ulen); + wpa_printf(MSG_DEBUG, + "FILS: HLP request UDP: sport=%u dport=%u ulen=%u sum=0x%x", + sport, dport, ulen, ntohs(udph->uh_sum)); + /* TODO: Check UDP checksum */ + if (ulen < sizeof(*udph) || ulen > len - sizeof(*iph)) + return 0; + + if (dport == DHCP_SERVER_PORT && sport == DHCP_CLIENT_PORT) { + return fils_process_hlp_dhcp(hapd, sta, (const u8 *) (udph + 1), + ulen - sizeof(*udph)); + } + + return 0; +} + + +static int fils_process_hlp_ip(struct hostapd_data *hapd, + struct sta_info *sta, const u8 *dst, + const u8 *pos, size_t len) +{ + const struct iphdr *iph; + u16 tot_len; + + if (len < sizeof(*iph)) + return 0; + iph = (const struct iphdr *) pos; + if (ip_checksum(iph, sizeof(*iph)) != 0) { + wpa_printf(MSG_DEBUG, + "FILS: HLP request IPv4 packet had invalid header checksum - dropped"); + return 0; + } + tot_len = ntohs(iph->tot_len); + if (tot_len > len) + return 0; + wpa_printf(MSG_DEBUG, + "FILS: HLP request IPv4: saddr=%08x daddr=%08x protocol=%u", + iph->saddr, iph->daddr, iph->protocol); + switch (iph->protocol) { + case 17: + return fils_process_hlp_udp(hapd, sta, dst, pos, len); + } + + return 0; +} + + +static int fils_process_hlp_req(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *pos, size_t len) +{ + const u8 *pkt, *end; + + wpa_printf(MSG_DEBUG, "FILS: HLP request from " MACSTR " (dst=" MACSTR + " src=" MACSTR " len=%u)", + MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN), + (unsigned int) len); + if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, + "FILS: Ignore HLP request with unexpected source address" + MACSTR, MAC2STR(pos + ETH_ALEN)); + return 0; + } + + end = pos + len; + pkt = pos + 2 * ETH_ALEN; + if (end - pkt >= 6 && + os_memcmp(pkt, "\xaa\xaa\x03\x00\x00\x00", 6) == 0) + pkt += 6; /* Remove SNAP/LLC header */ + wpa_hexdump(MSG_MSGDUMP, "FILS: HLP request packet", pkt, end - pkt); + + if (end - pkt < 2) + return 0; + + switch (WPA_GET_BE16(pkt)) { + case ETH_P_IP: + return fils_process_hlp_ip(hapd, sta, pos, pkt + 2, + end - pkt - 2); + } + + return 0; +} + + +int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *pos, int left) +{ + const u8 *end = pos + left; + u8 *tmp, *tmp_pos; + int ret = 0; + + /* Old DHCPDISCOVER is not needed anymore, if it was still pending */ + wpabuf_free(sta->hlp_dhcp_discover); + sta->hlp_dhcp_discover = NULL; + sta->fils_dhcp_rapid_commit_proxy = 0; + + /* Check if there are any FILS HLP Container elements */ + while (end - pos >= 2) { + if (2 + pos[1] > end - pos) + return 0; + if (pos[0] == WLAN_EID_EXTENSION && + pos[1] >= 1 + 2 * ETH_ALEN && + pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER) + break; + pos += 2 + pos[1]; + } + if (end - pos < 2) + return 0; /* No FILS HLP Container elements */ + + tmp = os_malloc(end - pos); + if (!tmp) + return 0; + + while (end - pos >= 2) { + if (2 + pos[1] > end - pos || + pos[0] != WLAN_EID_EXTENSION || + pos[1] < 1 + 2 * ETH_ALEN || + pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER) + break; + tmp_pos = tmp; + os_memcpy(tmp_pos, pos + 3, pos[1] - 1); + tmp_pos += pos[1] - 1; + pos += 2 + pos[1]; + + /* Add possible fragments */ + while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT && + 2 + pos[1] <= end - pos) { + os_memcpy(tmp_pos, pos + 2, pos[1]); + tmp_pos += pos[1]; + pos += 2 + pos[1]; + } + + if (fils_process_hlp_req(hapd, sta, tmp, tmp_pos - tmp) > 0) + ret = 1; + } + + os_free(tmp); + + return ret; +} + + +void fils_hlp_deinit(struct hostapd_data *hapd) +{ + if (hapd->dhcp_sock >= 0) { + eloop_unregister_read_sock(hapd->dhcp_sock); + close(hapd->dhcp_sock); + hapd->dhcp_sock = -1; + } +} diff --git a/src/ap/fils_hlp.h b/src/ap/fils_hlp.h new file mode 100644 index 000000000000..e14a6bf65e04 --- /dev/null +++ b/src/ap/fils_hlp.h @@ -0,0 +1,27 @@ +/* + * FILS HLP request processing + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FILS_HLP_H +#define FILS_HLP_H + +int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *pos, int left); + +#ifdef CONFIG_FILS + +void fils_hlp_deinit(struct hostapd_data *hapd); + +#else /* CONFIG_FILS */ + +static inline void fils_hlp_deinit(struct hostapd_data *hapd) +{ +} + +#endif /* CONFIG_FILS */ + +#endif /* FILS_HLP_H */ diff --git a/src/ap/gas_query_ap.c b/src/ap/gas_query_ap.c new file mode 100644 index 000000000000..fdb3cad55ade --- /dev/null +++ b/src/ap/gas_query_ap.c @@ -0,0 +1,714 @@ +/* + * Generic advertisement service (GAS) query (hostapd) + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011-2017, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "utils/eloop.h" +#include "utils/list.h" +#include "common/ieee802_11_defs.h" +#include "common/gas.h" +#include "common/wpa_ctrl.h" +#include "hostapd.h" +#include "sta_info.h" +#include "ap_drv_ops.h" +#include "gas_query_ap.h" + + +/** GAS query timeout in seconds */ +#define GAS_QUERY_TIMEOUT_PERIOD 2 + +/* GAS query wait-time / duration in ms */ +#define GAS_QUERY_WAIT_TIME_INITIAL 1000 +#define GAS_QUERY_WAIT_TIME_COMEBACK 150 + +/** + * struct gas_query_pending - Pending GAS query + */ +struct gas_query_pending { + struct dl_list list; + struct gas_query_ap *gas; + u8 addr[ETH_ALEN]; + u8 dialog_token; + u8 next_frag_id; + unsigned int wait_comeback:1; + unsigned int offchannel_tx_started:1; + unsigned int retry:1; + int freq; + u16 status_code; + struct wpabuf *req; + struct wpabuf *adv_proto; + struct wpabuf *resp; + struct os_reltime last_oper; + void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, + enum gas_query_ap_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code); + void *ctx; + u8 sa[ETH_ALEN]; +}; + +/** + * struct gas_query_ap - Internal GAS query data + */ +struct gas_query_ap { + struct hostapd_data *hapd; + void *msg_ctx; + struct dl_list pending; /* struct gas_query_pending */ + struct gas_query_pending *current; +}; + + +static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx); +static void gas_query_timeout(void *eloop_data, void *user_ctx); +static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx); +static void gas_query_tx_initial_req(struct gas_query_ap *gas, + struct gas_query_pending *query); +static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst); + + +static int ms_from_time(struct os_reltime *last) +{ + struct os_reltime now, res; + + os_get_reltime(&now); + os_reltime_sub(&now, last, &res); + return res.sec * 1000 + res.usec / 1000; +} + + +/** + * gas_query_ap_init - Initialize GAS query component + * @hapd: Pointer to hostapd data + * Returns: Pointer to GAS query data or %NULL on failure + */ +struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd, + void *msg_ctx) +{ + struct gas_query_ap *gas; + + gas = os_zalloc(sizeof(*gas)); + if (!gas) + return NULL; + + gas->hapd = hapd; + gas->msg_ctx = msg_ctx; + dl_list_init(&gas->pending); + + return gas; +} + + +static const char * gas_result_txt(enum gas_query_ap_result result) +{ + switch (result) { + case GAS_QUERY_AP_SUCCESS: + return "SUCCESS"; + case GAS_QUERY_AP_FAILURE: + return "FAILURE"; + case GAS_QUERY_AP_TIMEOUT: + return "TIMEOUT"; + case GAS_QUERY_AP_PEER_ERROR: + return "PEER_ERROR"; + case GAS_QUERY_AP_INTERNAL_ERROR: + return "INTERNAL_ERROR"; + case GAS_QUERY_AP_DELETED_AT_DEINIT: + return "DELETED_AT_DEINIT"; + } + + return "N/A"; +} + + +static void gas_query_free(struct gas_query_pending *query, int del_list) +{ + if (del_list) + dl_list_del(&query->list); + + wpabuf_free(query->req); + wpabuf_free(query->adv_proto); + wpabuf_free(query->resp); + os_free(query); +} + + +static void gas_query_done(struct gas_query_ap *gas, + struct gas_query_pending *query, + enum gas_query_ap_result result) +{ + wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR + " dialog_token=%u freq=%d status_code=%u result=%s", + MAC2STR(query->addr), query->dialog_token, query->freq, + query->status_code, gas_result_txt(result)); + if (gas->current == query) + gas->current = NULL; + eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); + eloop_cancel_timeout(gas_query_timeout, gas, query); + eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query); + dl_list_del(&query->list); + query->cb(query->ctx, query->addr, query->dialog_token, result, + query->adv_proto, query->resp, query->status_code); + gas_query_free(query, 0); +} + + +/** + * gas_query_ap_deinit - Deinitialize GAS query component + * @gas: GAS query data from gas_query_init() + */ +void gas_query_ap_deinit(struct gas_query_ap *gas) +{ + struct gas_query_pending *query, *next; + + if (gas == NULL) + return; + + dl_list_for_each_safe(query, next, &gas->pending, + struct gas_query_pending, list) + gas_query_done(gas, query, GAS_QUERY_AP_DELETED_AT_DEINIT); + + os_free(gas); +} + + +static struct gas_query_pending * +gas_query_get_pending(struct gas_query_ap *gas, const u8 *addr, u8 dialog_token) +{ + struct gas_query_pending *q; + dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) { + if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 && + q->dialog_token == dialog_token) + return q; + } + return NULL; +} + + +static int gas_query_append(struct gas_query_pending *query, const u8 *data, + size_t len) +{ + if (wpabuf_resize(&query->resp, len) < 0) { + wpa_printf(MSG_DEBUG, "GAS: No memory to store the response"); + return -1; + } + wpabuf_put_data(query->resp, data, len); + return 0; +} + + +void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst, + const u8 *data, size_t data_len, int ok) +{ + struct gas_query_pending *query; + int dur; + + if (!gas || !gas->current) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: dst=" MACSTR + " ok=%d - no query in progress", MAC2STR(dst), ok); + return; + } + + query = gas->current; + + dur = ms_from_time(&query->last_oper); + wpa_printf(MSG_DEBUG, "GAS: TX status: dst=" MACSTR + " ok=%d query=%p dialog_token=%u dur=%d ms", + MAC2STR(dst), ok, query, query->dialog_token, dur); + if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination"); + return; + } + os_get_reltime(&query->last_oper); + + eloop_cancel_timeout(gas_query_timeout, gas, query); + if (!ok) { + wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request"); + eloop_register_timeout(0, 250000, gas_query_timeout, + gas, query); + } else { + eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, + gas_query_timeout, gas, query); + } + if (query->wait_comeback && !query->retry) { + eloop_cancel_timeout(gas_query_rx_comeback_timeout, + gas, query); + eloop_register_timeout( + 0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000, + gas_query_rx_comeback_timeout, gas, query); + } +} + + +static int pmf_in_use(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + return sta && (sta->flags & WLAN_STA_MFP); +} + + +static int gas_query_tx(struct gas_query_ap *gas, + struct gas_query_pending *query, + struct wpabuf *req, unsigned int wait_time) +{ + int res, prot = pmf_in_use(gas->hapd, query->addr); + + wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u " + "freq=%d prot=%d using src addr " MACSTR, + MAC2STR(query->addr), (unsigned int) wpabuf_len(req), + query->freq, prot, MAC2STR(query->sa)); + if (prot) { + u8 *categ = wpabuf_mhead_u8(req); + *categ = WLAN_ACTION_PROTECTED_DUAL; + } + os_get_reltime(&query->last_oper); + res = hostapd_drv_send_action(gas->hapd, query->freq, wait_time, + query->addr, wpabuf_head(req), + wpabuf_len(req)); + return res; +} + + +static void gas_query_tx_comeback_req(struct gas_query_ap *gas, + struct gas_query_pending *query) +{ + struct wpabuf *req; + unsigned int wait_time; + + req = gas_build_comeback_req(query->dialog_token); + if (req == NULL) { + gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); + return; + } + + wait_time = (query->retry || !query->offchannel_tx_started) ? + GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK; + + if (gas_query_tx(gas, query, req, wait_time) < 0) { + wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " + MACSTR, MAC2STR(query->addr)); + gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); + } + + wpabuf_free(req); +} + + +static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx) +{ + struct gas_query_ap *gas = eloop_data; + struct gas_query_pending *query = user_ctx; + int dialog_token; + + wpa_printf(MSG_DEBUG, + "GAS: No response to comeback request received (retry=%u)", + query->retry); + if (gas->current != query || query->retry) + return; + dialog_token = gas_query_new_dialog_token(gas, query->addr); + if (dialog_token < 0) + return; + wpa_printf(MSG_DEBUG, + "GAS: Retry GAS query due to comeback response timeout"); + query->retry = 1; + query->dialog_token = dialog_token; + *(wpabuf_mhead_u8(query->req) + 2) = dialog_token; + query->wait_comeback = 0; + query->next_frag_id = 0; + wpabuf_free(query->adv_proto); + query->adv_proto = NULL; + eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); + eloop_cancel_timeout(gas_query_timeout, gas, query); + gas_query_tx_initial_req(gas, query); +} + + +static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx) +{ + struct gas_query_ap *gas = eloop_data; + struct gas_query_pending *query = user_ctx; + + wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR, + MAC2STR(query->addr)); + gas_query_tx_comeback_req(gas, query); +} + + +static void gas_query_tx_comeback_req_delay(struct gas_query_ap *gas, + struct gas_query_pending *query, + u16 comeback_delay) +{ + unsigned int secs, usecs; + + secs = (comeback_delay * 1024) / 1000000; + usecs = comeback_delay * 1024 - secs * 1000000; + wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR + " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs); + eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); + eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout, + gas, query); +} + + +static void gas_query_rx_initial(struct gas_query_ap *gas, + struct gas_query_pending *query, + const u8 *adv_proto, const u8 *resp, + size_t len, u16 comeback_delay) +{ + wpa_printf(MSG_DEBUG, "GAS: Received initial response from " + MACSTR " (dialog_token=%u comeback_delay=%u)", + MAC2STR(query->addr), query->dialog_token, comeback_delay); + + query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]); + if (query->adv_proto == NULL) { + gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); + return; + } + + if (comeback_delay) { + eloop_cancel_timeout(gas_query_timeout, gas, query); + query->wait_comeback = 1; + gas_query_tx_comeback_req_delay(gas, query, comeback_delay); + return; + } + + /* Query was completed without comeback mechanism */ + if (gas_query_append(query, resp, len) < 0) { + gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); + return; + } + + gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS); +} + + +static void gas_query_rx_comeback(struct gas_query_ap *gas, + struct gas_query_pending *query, + const u8 *adv_proto, const u8 *resp, + size_t len, u8 frag_id, u8 more_frags, + u16 comeback_delay) +{ + wpa_printf(MSG_DEBUG, "GAS: Received comeback response from " + MACSTR " (dialog_token=%u frag_id=%u more_frags=%u " + "comeback_delay=%u)", + MAC2STR(query->addr), query->dialog_token, frag_id, + more_frags, comeback_delay); + eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query); + + if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) || + os_memcmp(adv_proto, wpabuf_head(query->adv_proto), + wpabuf_len(query->adv_proto)) != 0) { + wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed " + "between initial and comeback response from " + MACSTR, MAC2STR(query->addr)); + gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR); + return; + } + + if (comeback_delay) { + if (frag_id) { + wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response " + "with non-zero frag_id and comeback_delay " + "from " MACSTR, MAC2STR(query->addr)); + gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR); + return; + } + gas_query_tx_comeback_req_delay(gas, query, comeback_delay); + return; + } + + if (frag_id != query->next_frag_id) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response " + "from " MACSTR, MAC2STR(query->addr)); + if (frag_id + 1 == query->next_frag_id) { + wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible " + "retry of previous fragment"); + return; + } + gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR); + return; + } + query->next_frag_id++; + + if (gas_query_append(query, resp, len) < 0) { + gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); + return; + } + + if (more_frags) { + gas_query_tx_comeback_req(gas, query); + return; + } + + gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS); +} + + +/** + * gas_query_ap_rx - Indicate reception of a Public Action or Protected Dual + * frame + * @gas: GAS query data from gas_query_init() + * @sa: Source MAC address of the Action frame + * @categ: Category of the Action frame + * @data: Payload of the Action frame + * @len: Length of @data + * @freq: Frequency (in MHz) on which the frame was received + * Returns: 0 if the Public Action frame was a GAS frame or -1 if not + */ +int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ, + const u8 *data, size_t len, int freq) +{ + struct gas_query_pending *query; + u8 action, dialog_token, frag_id = 0, more_frags = 0; + u16 comeback_delay, resp_len; + const u8 *pos, *adv_proto; + int prot, pmf; + unsigned int left; + + if (!gas || len < 4) + return -1; + + pos = data; + action = *pos++; + dialog_token = *pos++; + + if (action != WLAN_PA_GAS_INITIAL_RESP && + action != WLAN_PA_GAS_COMEBACK_RESP) + return -1; /* Not a GAS response */ + + prot = categ == WLAN_ACTION_PROTECTED_DUAL; + pmf = pmf_in_use(gas->hapd, sa); + if (prot && !pmf) { + wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled"); + return 0; + } + if (!prot && pmf) { + wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled"); + return 0; + } + + query = gas_query_get_pending(gas, sa, dialog_token); + if (query == NULL) { + wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR + " dialog token %u", MAC2STR(sa), dialog_token); + return -1; + } + + wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR, + ms_from_time(&query->last_oper), MAC2STR(sa)); + + if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from " + MACSTR " dialog token %u when waiting for comeback " + "response", MAC2STR(sa), dialog_token); + return 0; + } + + if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from " + MACSTR " dialog token %u when waiting for initial " + "response", MAC2STR(sa), dialog_token); + return 0; + } + + query->status_code = WPA_GET_LE16(pos); + pos += 2; + + if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING && + action == WLAN_PA_GAS_COMEBACK_RESP) { + wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response"); + } else if (query->status_code != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token " + "%u failed - status code %u", + MAC2STR(sa), dialog_token, query->status_code); + gas_query_done(gas, query, GAS_QUERY_AP_FAILURE); + return 0; + } + + if (action == WLAN_PA_GAS_COMEBACK_RESP) { + if (pos + 1 > data + len) + return 0; + frag_id = *pos & 0x7f; + more_frags = (*pos & 0x80) >> 7; + pos++; + } + + /* Comeback Delay */ + if (pos + 2 > data + len) + return 0; + comeback_delay = WPA_GET_LE16(pos); + pos += 2; + + /* Advertisement Protocol element */ + if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) { + wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement " + "Protocol element in the response from " MACSTR, + MAC2STR(sa)); + return 0; + } + + if (*pos != WLAN_EID_ADV_PROTO) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement " + "Protocol element ID %u in response from " MACSTR, + *pos, MAC2STR(sa)); + return 0; + } + + adv_proto = pos; + pos += 2 + pos[1]; + + /* Query Response Length */ + if (pos + 2 > data + len) { + wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length"); + return 0; + } + resp_len = WPA_GET_LE16(pos); + pos += 2; + + left = data + len - pos; + if (resp_len > left) { + wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in " + "response from " MACSTR, MAC2STR(sa)); + return 0; + } + + if (resp_len < left) { + wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data " + "after Query Response from " MACSTR, + left - resp_len, MAC2STR(sa)); + } + + if (action == WLAN_PA_GAS_COMEBACK_RESP) + gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len, + frag_id, more_frags, comeback_delay); + else + gas_query_rx_initial(gas, query, adv_proto, pos, resp_len, + comeback_delay); + + return 0; +} + + +static void gas_query_timeout(void *eloop_data, void *user_ctx) +{ + struct gas_query_ap *gas = eloop_data; + struct gas_query_pending *query = user_ctx; + + wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR + " dialog token %u", + MAC2STR(query->addr), query->dialog_token); + gas_query_done(gas, query, GAS_QUERY_AP_TIMEOUT); +} + + +static int gas_query_dialog_token_available(struct gas_query_ap *gas, + const u8 *dst, u8 dialog_token) +{ + struct gas_query_pending *q; + dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) { + if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 && + dialog_token == q->dialog_token) + return 0; + } + + return 1; +} + + +static void gas_query_tx_initial_req(struct gas_query_ap *gas, + struct gas_query_pending *query) +{ + if (gas_query_tx(gas, query, query->req, + GAS_QUERY_WAIT_TIME_INITIAL) < 0) { + wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " + MACSTR, MAC2STR(query->addr)); + gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); + return; + } + gas->current = query; + + wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u", + query->dialog_token); + eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, + gas_query_timeout, gas, query); +} + + +static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst) +{ + static int next_start = 0; + int dialog_token; + + for (dialog_token = 0; dialog_token < 256; dialog_token++) { + if (gas_query_dialog_token_available( + gas, dst, (next_start + dialog_token) % 256)) + break; + } + if (dialog_token == 256) + return -1; /* Too many pending queries */ + dialog_token = (next_start + dialog_token) % 256; + next_start = (dialog_token + 1) % 256; + return dialog_token; +} + + +/** + * gas_query_ap_req - Request a GAS query + * @gas: GAS query data from gas_query_init() + * @dst: Destination MAC address for the query + * @freq: Frequency (in MHz) for the channel on which to send the query + * @req: GAS query payload (to be freed by gas_query module in case of success + * return) + * @cb: Callback function for reporting GAS query result and response + * @ctx: Context pointer to use with the @cb call + * Returns: dialog token (>= 0) on success or -1 on failure + */ +int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq, + struct wpabuf *req, + void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, + enum gas_query_ap_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code), + void *ctx) +{ + struct gas_query_pending *query; + int dialog_token; + + if (!gas || wpabuf_len(req) < 3) + return -1; + + dialog_token = gas_query_new_dialog_token(gas, dst); + if (dialog_token < 0) + return -1; + + query = os_zalloc(sizeof(*query)); + if (query == NULL) + return -1; + + query->gas = gas; + os_memcpy(query->addr, dst, ETH_ALEN); + query->dialog_token = dialog_token; + query->freq = freq; + query->cb = cb; + query->ctx = ctx; + query->req = req; + dl_list_add(&gas->pending, &query->list); + + *(wpabuf_mhead_u8(req) + 2) = dialog_token; + + wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_START "addr=" MACSTR + " dialog_token=%u freq=%d", + MAC2STR(query->addr), query->dialog_token, query->freq); + + gas_query_tx_initial_req(gas, query); + + return dialog_token; +} diff --git a/src/ap/gas_query_ap.h b/src/ap/gas_query_ap.h new file mode 100644 index 000000000000..70f1f0537657 --- /dev/null +++ b/src/ap/gas_query_ap.h @@ -0,0 +1,43 @@ +/* + * Generic advertisement service (GAS) query + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011-2017, Qualcomm Atheros + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef GAS_QUERY_AP_H +#define GAS_QUERY_AP_H + +struct gas_query_ap; + +struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd, + void *msg_ctx); +void gas_query_ap_deinit(struct gas_query_ap *gas); +int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ, + const u8 *data, size_t len, int freq); + +/** + * enum gas_query_ap_result - GAS query result + */ +enum gas_query_ap_result { + GAS_QUERY_AP_SUCCESS, + GAS_QUERY_AP_FAILURE, + GAS_QUERY_AP_TIMEOUT, + GAS_QUERY_AP_PEER_ERROR, + GAS_QUERY_AP_INTERNAL_ERROR, + GAS_QUERY_AP_DELETED_AT_DEINIT +}; + +int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq, + struct wpabuf *req, + void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, + enum gas_query_ap_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code), + void *ctx); +void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst, + const u8 *data, size_t data_len, int ok); + +#endif /* GAS_QUERY_AP_H */ diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c index 6ce178de3b29..a7df81032477 100644 --- a/src/ap/gas_serv.c +++ b/src/ap/gas_serv.c @@ -11,14 +11,31 @@ #include "common.h" #include "common/ieee802_11_defs.h" #include "common/gas.h" +#include "common/wpa_ctrl.h" #include "utils/eloop.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" +#include "dpp_hostapd.h" #include "sta_info.h" #include "gas_serv.h" +#ifdef CONFIG_DPP +static void gas_serv_write_dpp_adv_proto(struct wpabuf *buf) +{ + wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(buf, 8); /* Length */ + wpabuf_put_u8(buf, 0x7f); + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 5); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, DPP_OUI_TYPE); + wpabuf_put_u8(buf, 0x01); +} +#endif /* CONFIG_DPP */ + + static void convert_to_protected_dual(struct wpabuf *msg) { u8 *categ = wpabuf_mhead_u8(msg); @@ -50,9 +67,12 @@ gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token) sta->flags |= WLAN_STA_GAS; /* * The default inactivity is 300 seconds. We don't need - * it to be that long. + * it to be that long. Use five second timeout and increase this + * with the comeback_delay for testing cases. */ - ap_sta_session_timeout(hapd, sta, 5); + ap_sta_session_timeout(hapd, sta, + hapd->conf->gas_comeback_delay / 1024 + + 5); } else { ap_sta_replenish_timeout(hapd, sta, 5); } @@ -161,8 +181,12 @@ static void anqp_add_hs_capab_list(struct hostapd_data *hapd, wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); if (hapd->conf->hs20_osu_providers_count) wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST); + if (hapd->conf->hs20_osu_providers_nai_count) + wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST); if (hapd->conf->hs20_icons_count) wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST); + if (hapd->conf->hs20_operator_icon_count) + wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA); gas_anqp_set_element_len(buf, len); } #endif /* CONFIG_HS20 */ @@ -255,20 +279,29 @@ static void anqp_add_capab_list(struct hostapd_data *hapd, wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI)) wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI); + if (get_anqp_elem(hapd, ANQP_TDLS_CAPABILITY)) + wpabuf_put_le16(buf, ANQP_TDLS_CAPABILITY); if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI)) wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI); if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT)) wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT); - for (id = 273; id < 277; id++) { - if (get_anqp_elem(hapd, id)) - wpabuf_put_le16(buf, id); - } - if (get_anqp_elem(hapd, ANQP_VENUE_URL)) +#ifdef CONFIG_FILS + if (!dl_list_empty(&hapd->conf->fils_realms) || + get_anqp_elem(hapd, ANQP_FILS_REALM_INFO)) + wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO); +#endif /* CONFIG_FILS */ + if (get_anqp_elem(hapd, ANQP_CAG)) + wpabuf_put_le16(buf, ANQP_CAG); + if (hapd->conf->venue_url || get_anqp_elem(hapd, ANQP_VENUE_URL)) wpabuf_put_le16(buf, ANQP_VENUE_URL); if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE)) wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE); if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT)) wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT); + for (id = 280; id < 300; id++) { + if (get_anqp_elem(hapd, id)) + wpabuf_put_le16(buf, id); + } #ifdef CONFIG_HS20 anqp_add_hs_capab_list(hapd, buf); #endif /* CONFIG_HS20 */ @@ -299,6 +332,29 @@ static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf) } +static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf) +{ + if (anqp_add_override(hapd, buf, ANQP_VENUE_URL)) + return; + + if (hapd->conf->venue_url) { + u8 *len; + unsigned int i; + + len = gas_anqp_add_element(buf, ANQP_VENUE_URL); + for (i = 0; i < hapd->conf->venue_url_count; i++) { + struct hostapd_venue_url *url; + + url = &hapd->conf->venue_url[i]; + wpabuf_put_u8(buf, 1 + url->url_len); + wpabuf_put_u8(buf, url->venue_number); + wpabuf_put_data(buf, url->url, url->url_len); + } + gas_anqp_set_element_len(buf, len); + } +} + + static void anqp_add_network_auth_type(struct hostapd_data *hapd, struct wpabuf *buf) { @@ -548,6 +604,36 @@ static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf) } +#ifdef CONFIG_FILS +static void anqp_add_fils_realm_info(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + size_t count; + + if (anqp_add_override(hapd, buf, ANQP_FILS_REALM_INFO)) + return; + + count = dl_list_len(&hapd->conf->fils_realms); + if (count > 10000) + count = 10000; + if (count) { + struct fils_realm *realm; + + wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO); + wpabuf_put_le16(buf, 2 * count); + + dl_list_for_each(realm, &hapd->conf->fils_realms, + struct fils_realm, list) { + if (count == 0) + break; + wpabuf_put_data(buf, realm->hash, 2); + count--; + } + } +} +#endif /* CONFIG_FILS */ + + #ifdef CONFIG_HS20 static void anqp_add_operator_friendly_name(struct hostapd_data *hapd, @@ -621,6 +707,29 @@ static void anqp_add_operating_class(struct hostapd_data *hapd, } +static void anqp_add_icon(struct wpabuf *buf, struct hostapd_bss_config *bss, + const char *name) +{ + size_t j; + struct hs20_icon *icon = NULL; + + for (j = 0; j < bss->hs20_icons_count && !icon; j++) { + if (os_strcmp(name, bss->hs20_icons[j].name) == 0) + icon = &bss->hs20_icons[j]; + } + if (!icon) + return; /* icon info not found */ + + wpabuf_put_le16(buf, icon->width); + wpabuf_put_le16(buf, icon->height); + wpabuf_put_data(buf, icon->language, 3); + wpabuf_put_u8(buf, os_strlen(icon->type)); + wpabuf_put_str(buf, icon->type); + wpabuf_put_u8(buf, os_strlen(icon->name)); + wpabuf_put_str(buf, icon->name); +} + + static void anqp_add_osu_provider(struct wpabuf *buf, struct hostapd_bss_config *bss, struct hs20_osu_provider *p) @@ -649,32 +758,14 @@ static void anqp_add_osu_provider(struct wpabuf *buf, /* OSU Method List */ count = wpabuf_put(buf, 1); - for (i = 0; p->method_list[i] >= 0; i++) + for (i = 0; p->method_list && p->method_list[i] >= 0; i++) wpabuf_put_u8(buf, p->method_list[i]); *count = i; /* Icons Available */ len2 = wpabuf_put(buf, 2); - for (i = 0; i < p->icons_count; i++) { - size_t j; - struct hs20_icon *icon = NULL; - - for (j = 0; j < bss->hs20_icons_count && !icon; j++) { - if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) == - 0) - icon = &bss->hs20_icons[j]; - } - if (!icon) - continue; /* icon info not found */ - - wpabuf_put_le16(buf, icon->width); - wpabuf_put_le16(buf, icon->height); - wpabuf_put_data(buf, icon->language, 3); - wpabuf_put_u8(buf, os_strlen(icon->type)); - wpabuf_put_str(buf, icon->type); - wpabuf_put_u8(buf, os_strlen(icon->name)); - wpabuf_put_str(buf, icon->name); - } + for (i = 0; i < p->icons_count; i++) + anqp_add_icon(buf, bss, p->icons[i]); WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2); /* OSU_NAI */ @@ -728,6 +819,40 @@ static void anqp_add_osu_providers_list(struct hostapd_data *hapd, } +static void anqp_add_osu_provider_nai(struct wpabuf *buf, + struct hs20_osu_provider *p) +{ + /* OSU_NAI for shared BSS (Single SSID) */ + if (p->osu_nai2) { + wpabuf_put_u8(buf, os_strlen(p->osu_nai2)); + wpabuf_put_str(buf, p->osu_nai2); + } else { + wpabuf_put_u8(buf, 0); + } +} + + +static void anqp_add_osu_providers_nai_list(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_osu_providers_nai_count) { + size_t i; + u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST); + wpabuf_put_u8(buf, 0); /* Reserved */ + + for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) { + anqp_add_osu_provider_nai( + buf, &hapd->conf->hs20_osu_providers[i]); + } + + gas_anqp_set_element_len(buf, len); + } +} + + static void anqp_add_icon_binary_file(struct hostapd_data *hapd, struct wpabuf *buf, const u8 *name, size_t name_len) @@ -783,9 +908,49 @@ static void anqp_add_icon_binary_file(struct hostapd_data *hapd, gas_anqp_set_element_len(buf, len); } + +static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + struct hostapd_bss_config *bss = hapd->conf; + size_t i; + u8 *len; + + if (!bss->hs20_operator_icon_count) + return; + + len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA); + wpabuf_put_u8(buf, 0); /* Reserved */ + + for (i = 0; i < bss->hs20_operator_icon_count; i++) + anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]); + + gas_anqp_set_element_len(buf, len); +} + #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO +static void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->mbo_cell_data_conn_pref >= 0) { + u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF); + wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref); + gas_anqp_set_element_len(buf, len); + } +} +#endif /* CONFIG_MBO */ + + static size_t anqp_get_required_len(struct hostapd_data *hapd, const u16 *infoid, unsigned int num_infoid) @@ -821,6 +986,10 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, len += 1000; if (request & ANQP_REQ_ICON_REQUEST) len += 65536; +#ifdef CONFIG_FILS + if (request & ANQP_FILS_REALM_INFO) + len += 2 * dl_list_len(&hapd->conf->fils_realms); +#endif /* CONFIG_FILS */ len += anqp_get_required_len(hapd, extra_req, num_extra_req); buf = wpabuf_alloc(len); @@ -860,8 +1029,19 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, if (request & ANQP_REQ_EMERGENCY_NAI) anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI); - for (i = 0; i < num_extra_req; i++) + for (i = 0; i < num_extra_req; i++) { +#ifdef CONFIG_FILS + if (extra_req[i] == ANQP_FILS_REALM_INFO) { + anqp_add_fils_realm_info(hapd, buf); + continue; + } +#endif /* CONFIG_FILS */ + if (extra_req[i] == ANQP_VENUE_URL) { + anqp_add_venue_url(hapd, buf); + continue; + } anqp_add_elem(hapd, buf, extra_req[i]); + } #ifdef CONFIG_HS20 if (request & ANQP_REQ_HS_CAPABILITY_LIST) @@ -878,8 +1058,17 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, anqp_add_osu_providers_list(hapd, buf); if (request & ANQP_REQ_ICON_REQUEST) anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len); + if (request & ANQP_REQ_OPERATOR_ICON_METADATA) + anqp_add_operator_icon_metadata(hapd, buf); + if (request & ANQP_REQ_OSU_PROVIDERS_NAI_LIST) + anqp_add_osu_providers_nai_list(hapd, buf); #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF) + anqp_add_mbo_cell_data_conn_pref(hapd, buf); +#endif /* CONFIG_MBO */ + return buf; } @@ -984,7 +1173,17 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id, get_anqp_elem(hapd, info_id) != NULL, qi); break; default: - if (!get_anqp_elem(hapd, info_id)) { +#ifdef CONFIG_FILS + if (info_id == ANQP_FILS_REALM_INFO && + !dl_list_empty(&hapd->conf->fils_realms)) { + wpa_printf(MSG_DEBUG, + "ANQP: FILS Realm Information (local)"); + } else +#endif /* CONFIG_FILS */ + if (info_id == ANQP_VENUE_URL && hapd->conf->venue_url) { + wpa_printf(MSG_DEBUG, + "ANQP: Venue URL (local)"); + } else if (!get_anqp_elem(hapd, info_id)) { wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", info_id); break; @@ -1050,6 +1249,16 @@ static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype, set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list", hapd->conf->hs20_osu_providers_count, qi); break; + case HS20_STYPE_OPERATOR_ICON_METADATA: + set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA, + "Operator Icon Metadata", + hapd->conf->hs20_operator_icon_count, qi); + break; + case HS20_STYPE_OSU_PROVIDERS_NAI_LIST: + set_anqp_req(ANQP_REQ_OSU_PROVIDERS_NAI_LIST, + "OSU Providers NAI List", + hapd->conf->hs20_osu_providers_nai_count, qi); + break; default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u", subtype); @@ -1092,49 +1301,12 @@ static void rx_anqp_hs_icon_request(struct hostapd_data *hapd, } -static void rx_anqp_vendor_specific(struct hostapd_data *hapd, - const u8 *pos, const u8 *end, - struct anqp_query_info *qi) +static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) { - u32 oui; u8 subtype; - if (end - pos < 4) { - wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " - "Query element"); - return; - } - - oui = WPA_GET_BE24(pos); - pos += 3; - if (oui != OUI_WFA) { - wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x", - oui); - return; - } - -#ifdef CONFIG_P2P - if (*pos == P2P_OUI_TYPE) { - /* - * This is for P2P SD and will be taken care of by the P2P - * implementation. This query needs to be ignored in the generic - * GAS server to avoid duplicated response. - */ - wpa_printf(MSG_DEBUG, - "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server", - *pos); - qi->p2p_sd = 1; - return; - } -#endif /* CONFIG_P2P */ - - if (*pos != HS20_ANQP_OUI_TYPE) { - wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", - *pos); - return; - } - pos++; - if (end - pos <= 1) return; @@ -1164,6 +1336,115 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd, #endif /* CONFIG_HS20 */ +#ifdef CONFIG_P2P +static void rx_anqp_vendor_specific_p2p(struct hostapd_data *hapd, + struct anqp_query_info *qi) +{ + /* + * This is for P2P SD and will be taken care of by the P2P + * implementation. This query needs to be ignored in the generic + * GAS server to avoid duplicated response. + */ + wpa_printf(MSG_DEBUG, + "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server", + P2P_OUI_TYPE); + qi->p2p_sd = 1; + return; +} +#endif /* CONFIG_P2P */ + + +#ifdef CONFIG_MBO + +static void rx_anqp_mbo_query_list(struct hostapd_data *hapd, u8 subtype, + struct anqp_query_info *qi) +{ + switch (subtype) { + case MBO_ANQP_SUBTYPE_CELL_CONN_PREF: + set_anqp_req(ANQP_REQ_MBO_CELL_DATA_CONN_PREF, + "Cellular Data Connection Preference", + hapd->conf->mbo_cell_data_conn_pref >= 0, qi); + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO subtype %u", + subtype); + break; + } +} + + +static void rx_anqp_vendor_specific_mbo(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + u8 subtype; + + if (end - pos < 1) + return; + + subtype = *pos++; + switch (subtype) { + case MBO_ANQP_SUBTYPE_QUERY_LIST: + wpa_printf(MSG_DEBUG, "ANQP: MBO Query List"); + while (pos < end) { + rx_anqp_mbo_query_list(hapd, *pos, qi); + pos++; + } + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO query subtype %u", + subtype); + break; + } +} + +#endif /* CONFIG_MBO */ + + +static void rx_anqp_vendor_specific(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + u32 oui; + + if (end - pos < 4) { + wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " + "Query element"); + return; + } + + oui = WPA_GET_BE24(pos); + pos += 3; + if (oui != OUI_WFA) { + wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x", + oui); + return; + } + + switch (*pos) { +#ifdef CONFIG_P2P + case P2P_OUI_TYPE: + rx_anqp_vendor_specific_p2p(hapd, qi); + break; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_HS20 + case HS20_ANQP_OUI_TYPE: + rx_anqp_vendor_specific_hs20(hapd, pos + 1, end, qi); + break; +#endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + case MBO_ANQP_OUI_TYPE: + rx_anqp_vendor_specific_mbo(hapd, pos + 1, end, qi); + break; +#endif /* CONFIG_MBO */ + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", + *pos); + break; + } +} + + static void gas_serv_req_local_processing(struct hostapd_data *hapd, const u8 *sa, u8 dialog_token, struct anqp_query_info *qi, int prot, @@ -1189,7 +1470,7 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd, } #endif /* CONFIG_P2P */ - if (wpabuf_len(buf) > hapd->gas_frag_limit || + if (wpabuf_len(buf) > hapd->conf->gas_frag_limit || hapd->conf->gas_comeback_delay) { struct gas_dialog_info *di; u16 comeback_delay = 1; @@ -1240,6 +1521,72 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd, } +#ifdef CONFIG_DPP +static void gas_serv_req_dpp_processing(struct hostapd_data *hapd, + const u8 *sa, u8 dialog_token, + int prot, struct wpabuf *buf) +{ + struct wpabuf *tx_buf; + + if (wpabuf_len(buf) > hapd->conf->gas_frag_limit || + hapd->conf->gas_comeback_delay) { + struct gas_dialog_info *di; + u16 comeback_delay = 1; + + if (hapd->conf->gas_comeback_delay) { + /* Testing - allow overriding of the delay value */ + comeback_delay = hapd->conf->gas_comeback_delay; + } + + wpa_printf(MSG_DEBUG, + "DPP: Too long response to fit in initial response - use GAS comeback"); + di = gas_dialog_create(hapd, sa, dialog_token); + if (!di) { + wpa_printf(MSG_INFO, "DPP: Could not create dialog for " + MACSTR " (dialog token %u)", + MAC2STR(sa), dialog_token); + wpabuf_free(buf); + tx_buf = gas_build_initial_resp( + dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE, + 0, 10); + if (tx_buf) + gas_serv_write_dpp_adv_proto(tx_buf); + } else { + di->prot = prot; + di->sd_resp = buf; + di->sd_resp_pos = 0; + tx_buf = gas_build_initial_resp( + dialog_token, WLAN_STATUS_SUCCESS, + comeback_delay, 10); + if (tx_buf) + gas_serv_write_dpp_adv_proto(tx_buf); + } + } else { + wpa_printf(MSG_DEBUG, + "DPP: GAS Initial response (no comeback)"); + tx_buf = gas_build_initial_resp( + dialog_token, WLAN_STATUS_SUCCESS, 0, + 10 + 2 + wpabuf_len(buf)); + if (tx_buf) { + gas_serv_write_dpp_adv_proto(tx_buf); + wpabuf_put_le16(tx_buf, wpabuf_len(buf)); + wpabuf_put_buf(tx_buf, buf); + hostapd_dpp_gas_status_handler(hapd, 1); + } + wpabuf_free(buf); + } + if (!tx_buf) + return; + if (prot) + convert_to_protected_dual(tx_buf); + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); + wpabuf_free(tx_buf); +} +#endif /* CONFIG_DPP */ + + static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, const u8 *sa, const u8 *data, size_t len, int prot, @@ -1252,6 +1599,9 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, u16 slen; struct anqp_query_info qi; const u8 *adv_proto; +#ifdef CONFIG_DPP + int dpp = 0; +#endif /* CONFIG_DPP */ if (len < 1 + 2) return; @@ -1279,6 +1629,15 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, next = pos + slen; pos++; /* skip QueryRespLenLimit and PAME-BI */ +#ifdef CONFIG_DPP + if (slen == 8 && *pos == WLAN_EID_VENDOR_SPECIFIC && + pos[1] == 5 && WPA_GET_BE24(&pos[2]) == OUI_WFA && + pos[5] == DPP_OUI_TYPE && pos[6] == 0x01) { + wpa_printf(MSG_DEBUG, "DPP: Configuration Request"); + dpp = 1; + } else +#endif /* CONFIG_DPP */ + if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { struct wpabuf *buf; wpa_msg(hapd->msg_ctx, MSG_DEBUG, @@ -1318,6 +1677,18 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, return; end = pos + slen; +#ifdef CONFIG_DPP + if (dpp) { + struct wpabuf *msg; + + msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen); + if (!msg) + return; + gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg); + return; + } +#endif /* CONFIG_DPP */ + /* ANQP Query Request */ while (pos < end) { u16 info_id, elen; @@ -1339,11 +1710,9 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, case ANQP_QUERY_LIST: rx_anqp_query_list(hapd, pos, pos + elen, &qi); break; -#ifdef CONFIG_HS20 case ANQP_VENDOR_SPECIFIC: rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi); break; -#endif /* CONFIG_HS20 */ default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query " "Request element %u", info_id); @@ -1393,8 +1762,8 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, } frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; - if (frag_len > hapd->gas_frag_limit) { - frag_len = hapd->gas_frag_limit; + if (frag_len > hapd->conf->gas_frag_limit) { + frag_len = hapd->conf->gas_frag_limit; more = 1; } wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u", @@ -1407,6 +1776,18 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, gas_serv_dialog_clear(dialog); return; } +#ifdef CONFIG_DPP + if (dialog->dpp) { + tx_buf = gas_build_comeback_resp(dialog_token, + WLAN_STATUS_SUCCESS, + dialog->sd_frag_id, more, 0, + 10 + frag_len); + if (tx_buf) { + gas_serv_write_dpp_adv_proto(tx_buf); + wpabuf_put_buf(tx_buf, buf); + } + } else +#endif /* CONFIG_DPP */ tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token, WLAN_STATUS_SUCCESS, dialog->sd_frag_id, @@ -1430,6 +1811,10 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, } else { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of " "SD response sent"); +#ifdef CONFIG_DPP + if (dialog->dpp) + hostapd_dpp_gas_status_handler(hapd, 1); +#endif /* CONFIG_DPP */ gas_serv_dialog_clear(dialog); gas_serv_free_dialogs(hapd, sa); } @@ -1495,9 +1880,6 @@ int gas_serv_init(struct hostapd_data *hapd) { hapd->public_action_cb2 = gas_serv_rx_public_action; hapd->public_action_cb2_ctx = hapd; - hapd->gas_frag_limit = 1400; - if (hapd->conf->gas_frag_limit > 0) - hapd->gas_frag_limit = hapd->conf->gas_frag_limit; return 0; } diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h index 9051e4f90513..2cf1817298f7 100644 --- a/src/ap/gas_serv.h +++ b/src/ap/gas_serv.h @@ -41,7 +41,7 @@ #define ANQP_REQ_EMERGENCY_NAI \ (1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST)) /* - * First 16 Hotspot 2.0 vendor specific ANQP-elements can be included in the + * First 15 Hotspot 2.0 vendor specific ANQP-elements can be included in the * optimized bitmap. */ #define ANQP_REQ_HS_CAPABILITY_LIST \ @@ -60,6 +60,13 @@ (0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST) #define ANQP_REQ_ICON_REQUEST \ (0x10000 << HS20_STYPE_ICON_REQUEST) +#define ANQP_REQ_OPERATOR_ICON_METADATA \ + (0x10000 << HS20_STYPE_OPERATOR_ICON_METADATA) +#define ANQP_REQ_OSU_PROVIDERS_NAI_LIST \ + (0x10000 << HS20_STYPE_OSU_PROVIDERS_NAI_LIST) +/* The first MBO ANQP-element can be included in the optimized bitmap. */ +#define ANQP_REQ_MBO_CELL_DATA_CONN_PREF \ + (BIT(29) << MBO_ANQP_SUBTYPE_CELL_CONN_PREF) struct gas_dialog_info { u8 valid; @@ -68,6 +75,7 @@ struct gas_dialog_info { size_t sd_resp_pos; /* Offset in sd_resp */ u8 sd_frag_id; int prot; /* whether Protected Dual of Public Action frame is used */ + int dpp; /* whether this is a DPP Config Response */ }; struct hostapd_data; diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 9fafc7f457bb..7501bac6e42a 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -31,6 +31,8 @@ #include "vlan_init.h" #include "wpa_auth.h" #include "wps_hostapd.h" +#include "dpp_hostapd.h" +#include "gas_query_ap.h" #include "hw_features.h" #include "wpa_auth_glue.h" #include "ap_drv_ops.h" @@ -45,6 +47,9 @@ #include "ndisc_snoop.h" #include "neighbor_db.h" #include "rrm.h" +#include "fils_hlp.h" +#include "acs.h" +#include "hs20.h" static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); @@ -52,6 +57,8 @@ static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd); static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd); static int setup_interface2(struct hostapd_iface *iface); static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx); +static void hostapd_interface_setup_failure_handler(void *eloop_ctx, + void *timeout_ctx); int hostapd_for_each_interface(struct hapd_interfaces *interfaces, @@ -71,10 +78,26 @@ int hostapd_for_each_interface(struct hapd_interfaces *interfaces, } +void hostapd_reconfig_encryption(struct hostapd_data *hapd) +{ + if (hapd->wpa_auth) + return; + + hostapd_set_privacy(hapd, 0); + hostapd_setup_encryption(hapd->conf->iface, hapd); +} + + static void hostapd_reload_bss(struct hostapd_data *hapd) { struct hostapd_ssid *ssid; + if (!hapd->started) + return; + + if (hapd->conf->wmm_enabled < 0) + hapd->conf->wmm_enabled = hapd->iconf->ieee80211n; + #ifndef CONFIG_NO_RADIUS radius_client_reconfig(hapd->radius, hapd->conf->radius); #endif /* CONFIG_NO_RADIUS */ @@ -153,8 +176,27 @@ static void hostapd_clear_old(struct hostapd_iface *iface) } +static int hostapd_iface_conf_changed(struct hostapd_config *newconf, + struct hostapd_config *oldconf) +{ + size_t i; + + if (newconf->num_bss != oldconf->num_bss) + return 1; + + for (i = 0; i < newconf->num_bss; i++) { + if (os_strcmp(newconf->bss[i]->iface, + oldconf->bss[i]->iface) != 0) + return 1; + } + + return 0; +} + + int hostapd_reload_config(struct hostapd_iface *iface) { + struct hapd_interfaces *interfaces = iface->interfaces; struct hostapd_data *hapd = iface->bss[0]; struct hostapd_config *newconf, *oldconf; size_t j; @@ -177,6 +219,35 @@ int hostapd_reload_config(struct hostapd_iface *iface) hostapd_clear_old(iface); oldconf = hapd->iconf; + if (hostapd_iface_conf_changed(newconf, oldconf)) { + char *fname; + int res; + + wpa_printf(MSG_DEBUG, + "Configuration changes include interface/BSS modification - force full disable+enable sequence"); + fname = os_strdup(iface->config_fname); + if (!fname) { + hostapd_config_free(newconf); + return -1; + } + hostapd_remove_iface(interfaces, hapd->conf->iface); + iface = hostapd_init(interfaces, fname); + os_free(fname); + hostapd_config_free(newconf); + if (!iface) { + wpa_printf(MSG_ERROR, + "Failed to initialize interface on config reload"); + return -1; + } + iface->interfaces = interfaces; + interfaces->iface[interfaces->count] = iface; + interfaces->count++; + res = hostapd_enable_iface(iface); + if (res < 0) + wpa_printf(MSG_ERROR, + "Failed to enable interface on config reload"); + return res; + } iface->conf = newconf; for (j = 0; j < iface->num_bss; j++) { @@ -210,7 +281,7 @@ static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd, { int i; - if (!ifname) + if (!ifname || !hapd->drv_priv) return; for (i = 0; i < NUM_WEP_KEYS; i++) { if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, @@ -297,6 +368,10 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) #endif /* CONFIG_NO_RADIUS */ hostapd_deinit_wps(hapd); +#ifdef CONFIG_DPP + hostapd_dpp_deinit(hapd); + gas_query_ap_deinit(hapd->gas); +#endif /* CONFIG_DPP */ authsrv_deinit(hapd); @@ -341,6 +416,7 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) #endif /* CONFIG_MESH */ hostapd_clean_rrm(hapd); + fils_hlp_deinit(hapd); } @@ -357,8 +433,10 @@ static void hostapd_cleanup(struct hostapd_data *hapd) wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd, hapd->conf->iface); if (hapd->iface->interfaces && - hapd->iface->interfaces->ctrl_iface_deinit) + hapd->iface->interfaces->ctrl_iface_deinit) { + wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_TERMINATING); hapd->iface->interfaces->ctrl_iface_deinit(hapd); + } hostapd_free_hapd_data(hapd); } @@ -387,8 +465,11 @@ static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) hostapd_stop_setup_timers(iface); #endif /* NEED_AP_MLME */ #endif /* CONFIG_IEEE80211N */ + if (iface->current_mode) + acs_cleanup(iface); hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); iface->hw_features = NULL; + iface->current_mode = NULL; os_free(iface->current_rates); iface->current_rates = NULL; os_free(iface->basic_rates); @@ -409,6 +490,8 @@ static void hostapd_cleanup_iface(struct hostapd_iface *iface) { wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); + eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface, + NULL); hostapd_cleanup_iface_partial(iface); hostapd_config_free(iface->conf); @@ -484,9 +567,12 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason) ret = -1; } } - wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations"); - os_memset(addr, 0xff, ETH_ALEN); - hostapd_drv_sta_deauth(hapd, addr, reason); + if (hapd->conf && hapd->conf->broadcast_deauth) { + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "Deauthenticate all stations"); + os_memset(addr, 0xff, ETH_ALEN); + hostapd_drv_sta_deauth(hapd, addr, reason); + } hostapd_free_stas(hapd); return ret; @@ -873,6 +959,48 @@ hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr) return RADIUS_DAS_SUCCESS; } + +#ifdef CONFIG_HS20 +static enum radius_das_res +hostapd_das_coa(void *ctx, struct radius_das_attrs *attr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + int multi; + + if (hostapd_das_nas_mismatch(hapd, attr)) + return RADIUS_DAS_NAS_MISMATCH; + + sta = hostapd_das_find_sta(hapd, attr, &multi); + if (!sta) { + if (multi) { + wpa_printf(MSG_DEBUG, + "RADIUS DAS: Multiple sessions match - not supported"); + return RADIUS_DAS_MULTI_SESSION_MATCH; + } + wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found"); + return RADIUS_DAS_SESSION_NOT_FOUND; + } + + wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR + " - CoA", MAC2STR(sta->addr)); + + if (attr->hs20_t_c_filtering) { + if (attr->hs20_t_c_filtering[0] & BIT(0)) { + wpa_printf(MSG_DEBUG, + "HS 2.0: Unexpected Terms and Conditions filtering required in CoA-Request"); + return RADIUS_DAS_COA_FAILED; + } + + hs20_t_c_filtering(hapd, sta, 0); + } + + return RADIUS_DAS_SUCCESS; +} +#else /* CONFIG_HS20 */ +#define hostapd_das_coa NULL +#endif /* CONFIG_HS20 */ + #endif /* CONFIG_NO_RADIUS */ @@ -956,13 +1084,13 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) if (conf->wmm_enabled < 0) conf->wmm_enabled = hapd->iconf->ieee80211n; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (is_zero_ether_addr(conf->r1_key_holder)) os_memcpy(conf->r1_key_holder, hapd->own_addr, ETH_ALEN); -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_MESH - if (hapd->iface->mconf == NULL) + if ((hapd->conf->mesh & MESH_ENABLED) && hapd->iface->mconf == NULL) flush_old_stations = 0; #endif /* CONFIG_MESH */ @@ -1047,6 +1175,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) conf->radius_das_require_message_authenticator; das_conf.ctx = hapd; das_conf.disconnect = hostapd_das_disconnect; + das_conf.coa = hostapd_das_coa; hapd->radius_das = radius_das_init(&das_conf); if (hapd->radius_das == NULL) { wpa_printf(MSG_ERROR, "RADIUS DAS initialization " @@ -1063,6 +1192,14 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) if (hostapd_init_wps(hapd, conf)) return -1; +#ifdef CONFIG_DPP + hapd->gas = gas_query_ap_init(hapd, hapd->msg_ctx); + if (!hapd->gas) + return -1; + if (hostapd_dpp_init(hapd)) + return -1; +#endif /* CONFIG_DPP */ + if (authsrv_init(hapd) < 0) return -1; @@ -1150,7 +1287,7 @@ static void hostapd_tx_queue_params(struct hostapd_iface *iface) struct hostapd_tx_queue_params *p; #ifdef CONFIG_MESH - if (iface->mconf == NULL) + if ((hapd->conf->mesh & MESH_ENABLED) && iface->mconf == NULL) return; #endif /* CONFIG_MESH */ @@ -1561,7 +1698,7 @@ static void hostapd_set_own_neighbor_report(struct hostapd_data *hapd) int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac; struct wpa_ssid_value ssid; u8 channel, op_class; - int center_freq1 = 0, center_freq2 = 0; + u8 center_freq1_idx = 0, center_freq2_idx = 0; enum nr_chan_width width; u32 bssid_info; struct wpabuf *nr; @@ -1598,22 +1735,22 @@ static void hostapd_set_own_neighbor_report(struct hostapd_data *hapd) /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */ - ieee80211_freq_to_channel_ext(hapd->iface->freq, - hapd->iconf->secondary_channel, - hapd->iconf->vht_oper_chwidth, - &op_class, &channel); + if (ieee80211_freq_to_channel_ext(hapd->iface->freq, + hapd->iconf->secondary_channel, + hapd->iconf->vht_oper_chwidth, + &op_class, &channel) == + NUM_HOSTAPD_MODES) + return; width = hostapd_get_nr_chan_width(hapd, ht, vht); if (vht) { - center_freq1 = ieee80211_chan_to_freq( - NULL, op_class, - hapd->iconf->vht_oper_centr_freq_seg0_idx); + center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx; if (width == NR_CHAN_WIDTH_80P80) - center_freq2 = ieee80211_chan_to_freq( - NULL, op_class, - hapd->iconf->vht_oper_centr_freq_seg1_idx); + center_freq2_idx = + hapd->iconf->vht_oper_centr_freq_seg1_idx; } else if (ht) { - center_freq1 = hapd->iface->freq + - 10 * hapd->iconf->secondary_channel; + ieee80211_freq_to_chan(hapd->iface->freq + + 10 * hapd->iconf->secondary_channel, + ¢er_freq1_idx); } ssid.ssid_len = hapd->conf->ssid.ssid_len; @@ -1641,17 +1778,127 @@ static void hostapd_set_own_neighbor_report(struct hostapd_data *hapd) wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN); wpabuf_put_u8(nr, 3); wpabuf_put_u8(nr, width); - wpabuf_put_u8(nr, center_freq1); - wpabuf_put_u8(nr, center_freq2); + wpabuf_put_u8(nr, center_freq1_idx); + wpabuf_put_u8(nr, center_freq2_idx); hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci, - hapd->iconf->civic); + hapd->iconf->civic, hapd->iconf->stationary_ap); wpabuf_free(nr); #endif /* NEED_AP_MLME */ } +#ifdef CONFIG_OWE + +static int hostapd_owe_iface_iter(struct hostapd_iface *iface, void *ctx) +{ + struct hostapd_data *hapd = ctx; + size_t i; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss = iface->bss[i]; + + if (os_strcmp(hapd->conf->owe_transition_ifname, + bss->conf->iface) != 0) + continue; + + wpa_printf(MSG_DEBUG, + "OWE: ifname=%s found transition mode ifname=%s BSSID " + MACSTR " SSID %s", + hapd->conf->iface, bss->conf->iface, + MAC2STR(bss->own_addr), + wpa_ssid_txt(bss->conf->ssid.ssid, + bss->conf->ssid.ssid_len)); + if (!bss->conf->ssid.ssid_set || !bss->conf->ssid.ssid_len || + is_zero_ether_addr(bss->own_addr)) + continue; + + os_memcpy(hapd->conf->owe_transition_bssid, bss->own_addr, + ETH_ALEN); + os_memcpy(hapd->conf->owe_transition_ssid, + bss->conf->ssid.ssid, bss->conf->ssid.ssid_len); + hapd->conf->owe_transition_ssid_len = bss->conf->ssid.ssid_len; + wpa_printf(MSG_DEBUG, + "OWE: Copied transition mode information"); + return 1; + } + + return 0; +} + + +int hostapd_owe_trans_get_info(struct hostapd_data *hapd) +{ + if (hapd->conf->owe_transition_ssid_len > 0 && + !is_zero_ether_addr(hapd->conf->owe_transition_bssid)) + return 0; + + /* Find transition mode SSID/BSSID information from a BSS operated by + * this hostapd instance. */ + if (!hapd->iface->interfaces || + !hapd->iface->interfaces->for_each_interface) + return hostapd_owe_iface_iter(hapd->iface, hapd); + else + return hapd->iface->interfaces->for_each_interface( + hapd->iface->interfaces, hostapd_owe_iface_iter, hapd); +} + + +static int hostapd_owe_iface_iter2(struct hostapd_iface *iface, void *ctx) +{ + size_t i; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss = iface->bss[i]; + int res; + + if (!bss->conf->owe_transition_ifname[0]) + continue; + res = hostapd_owe_trans_get_info(bss); + if (res == 0) + continue; + wpa_printf(MSG_DEBUG, + "OWE: Matching transition mode interface enabled - update beacon data for %s", + bss->conf->iface); + ieee802_11_set_beacon(bss); + } + + return 0; +} + +#endif /* CONFIG_OWE */ + + +static void hostapd_owe_update_trans(struct hostapd_iface *iface) +{ +#ifdef CONFIG_OWE + /* Check whether the enabled BSS can complete OWE transition mode + * configuration for any pending interface. */ + if (!iface->interfaces || + !iface->interfaces->for_each_interface) + hostapd_owe_iface_iter2(iface, NULL); + else + iface->interfaces->for_each_interface( + iface->interfaces, hostapd_owe_iface_iter2, NULL); +#endif /* CONFIG_OWE */ +} + + +static void hostapd_interface_setup_failure_handler(void *eloop_ctx, + void *timeout_ctx) +{ + struct hostapd_iface *iface = eloop_ctx; + struct hostapd_data *hapd; + + if (iface->num_bss < 1 || !iface->bss || !iface->bss[0]) + return; + hapd = iface->bss[0]; + if (hapd->setup_complete_cb) + hapd->setup_complete_cb(hapd->setup_complete_cb_ctx); +} + + static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, int err) { @@ -1827,6 +2074,7 @@ dfs_offload: #endif /* CONFIG_FST */ hostapd_set_state(iface, HAPD_IFACE_ENABLED); + hostapd_owe_update_trans(iface); wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED); if (hapd->setup_complete_cb) hapd->setup_complete_cb(hapd->setup_complete_cb_ctx); @@ -1851,8 +2099,19 @@ fail: iface->fst = NULL; } #endif /* CONFIG_FST */ - if (iface->interfaces && iface->interfaces->terminate_on_error) + + if (iface->interfaces && iface->interfaces->terminate_on_error) { eloop_terminate(); + } else if (hapd->setup_complete_cb) { + /* + * Calling hapd->setup_complete_cb directly may cause iface + * deinitialization which may be accessed later by the caller. + */ + eloop_register_timeout(0, 0, + hostapd_interface_setup_failure_handler, + iface, NULL); + } + return -1; } @@ -1997,10 +2256,16 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, hapd->iconf = conf; hapd->conf = bss; hapd->iface = hapd_iface; - hapd->driver = hapd->iconf->driver; + if (conf) + hapd->driver = conf->driver; hapd->ctrl_sock = -1; dl_list_init(&hapd->ctrl_dst); dl_list_init(&hapd->nr_db); + hapd->dhcp_sock = -1; +#ifdef CONFIG_IEEE80211R_AP + dl_list_init(&hapd->l2_queue); + dl_list_init(&hapd->l2_oui_queue); +#endif /* CONFIG_IEEE80211R_AP */ return hapd; } @@ -2028,12 +2293,6 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) hostapd_set_state(iface, HAPD_IFACE_DISABLED); -#ifdef CONFIG_IEEE80211N -#ifdef NEED_AP_MLME - hostapd_stop_setup_timers(iface); - eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL); -#endif /* NEED_AP_MLME */ -#endif /* CONFIG_IEEE80211N */ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); iface->wait_channel_update = 0; @@ -2049,6 +2308,13 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) break; hostapd_bss_deinit(iface->bss[j]); } + +#ifdef CONFIG_IEEE80211N +#ifdef NEED_AP_MLME + hostapd_stop_setup_timers(iface); + eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL); +#endif /* NEED_AP_MLME */ +#endif /* CONFIG_IEEE80211N */ } @@ -2402,6 +2668,11 @@ int hostapd_disable_iface(struct hostapd_iface *hapd_iface) !!(hapd_iface->drv_flags & WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT); +#ifdef NEED_AP_MLME + for (j = 0; j < hapd_iface->num_bss; j++) + hostapd_cleanup_cs_params(hapd_iface->bss[j]); +#endif /* NEED_AP_MLME */ + /* same as hostapd_interface_deinit without deinitializing ctrl-iface */ for (j = 0; j < hapd_iface->num_bss; j++) { struct hostapd_data *hapd = hapd_iface->bss[j]; @@ -2459,7 +2730,7 @@ hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname, if (conf == NULL) { wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " "configuration", __func__); - return NULL; + return NULL; } if (driver) { @@ -2612,6 +2883,7 @@ int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf) return -1; } } + hostapd_owe_update_trans(hapd_iface); return 0; } @@ -2829,12 +3101,24 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, ieee802_1x_new_station(hapd, sta); if (reassoc) { if (sta->auth_alg != WLAN_AUTH_FT && + sta->auth_alg != WLAN_AUTH_FILS_SK && + sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && + sta->auth_alg != WLAN_AUTH_FILS_PK && !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH); } else wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm); - if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) { + if (eloop_cancel_timeout(ap_handle_timer, hapd, sta) > 0) { + wpa_printf(MSG_DEBUG, + "%s: %s: canceled wired ap_handle_timer timeout for " + MACSTR, + hapd->conf->iface, __func__, + MAC2STR(sta->addr)); + } + } else if (!(hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { wpa_printf(MSG_DEBUG, "%s: %s: reschedule ap_handle_timer timeout for " MACSTR " (%d seconds - ap_max_inactivity)", @@ -2928,60 +3212,52 @@ static int hostapd_build_beacon_data(struct hostapd_data *hapd, goto free_ap_params; ret = -1; - beacon->head = os_malloc(params.head_len); + beacon->head = os_memdup(params.head, params.head_len); if (!beacon->head) goto free_ap_extra_ies; - os_memcpy(beacon->head, params.head, params.head_len); beacon->head_len = params.head_len; - beacon->tail = os_malloc(params.tail_len); + beacon->tail = os_memdup(params.tail, params.tail_len); if (!beacon->tail) goto free_beacon; - os_memcpy(beacon->tail, params.tail, params.tail_len); beacon->tail_len = params.tail_len; if (params.proberesp != NULL) { - beacon->probe_resp = os_malloc(params.proberesp_len); + beacon->probe_resp = os_memdup(params.proberesp, + params.proberesp_len); if (!beacon->probe_resp) goto free_beacon; - os_memcpy(beacon->probe_resp, params.proberesp, - params.proberesp_len); beacon->probe_resp_len = params.proberesp_len; } /* copy the extra ies */ if (beacon_extra) { - beacon->beacon_ies = os_malloc(wpabuf_len(beacon_extra)); + beacon->beacon_ies = os_memdup(beacon_extra->buf, + wpabuf_len(beacon_extra)); if (!beacon->beacon_ies) goto free_beacon; - os_memcpy(beacon->beacon_ies, - beacon_extra->buf, wpabuf_len(beacon_extra)); beacon->beacon_ies_len = wpabuf_len(beacon_extra); } if (proberesp_extra) { - beacon->proberesp_ies = - os_malloc(wpabuf_len(proberesp_extra)); + beacon->proberesp_ies = os_memdup(proberesp_extra->buf, + wpabuf_len(proberesp_extra)); if (!beacon->proberesp_ies) goto free_beacon; - os_memcpy(beacon->proberesp_ies, proberesp_extra->buf, - wpabuf_len(proberesp_extra)); beacon->proberesp_ies_len = wpabuf_len(proberesp_extra); } if (assocresp_extra) { - beacon->assocresp_ies = - os_malloc(wpabuf_len(assocresp_extra)); + beacon->assocresp_ies = os_memdup(assocresp_extra->buf, + wpabuf_len(assocresp_extra)); if (!beacon->assocresp_ies) goto free_beacon; - os_memcpy(beacon->assocresp_ies, assocresp_extra->buf, - wpabuf_len(assocresp_extra)); beacon->assocresp_ies_len = wpabuf_len(assocresp_extra); } @@ -3158,6 +3434,19 @@ void hostapd_cleanup_cs_params(struct hostapd_data *hapd) } +void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled) +{ + if (vht_enabled) + hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_ENABLED; + else + hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_DISABLED; + + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "CHAN_SWITCH VHT CONFIG 0x%x", + hapd->iconf->ch_switch_vht_config); +} + + int hostapd_switch_channel(struct hostapd_data *hapd, struct csa_settings *settings) { @@ -3192,7 +3481,6 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface, const struct hostapd_freq_params *freq_params) { int vht_seg0_idx = 0, vht_seg1_idx = 0, vht_bw = VHT_CHANWIDTH_USE_HT; - unsigned int i; wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes"); @@ -3234,10 +3522,8 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface, /* * cs_params must not be cleared earlier because the freq_params * argument may actually point to one of these. + * These params will be cleared during interface disable below. */ - for (i = 0; i < iface->num_bss; i++) - hostapd_cleanup_cs_params(iface->bss[i]); - hostapd_disable_iface(iface); hostapd_enable_iface(iface); } diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index dec46f692206..d304c1171810 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -14,6 +14,13 @@ #include "ap_config.h" #include "drivers/driver.h" +#define OCE_STA_CFON_ENABLED(hapd) \ + ((hapd->conf->oce & OCE_STA_CFON) && \ + (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON)) +#define OCE_AP_ENABLED(hapd) \ + ((hapd->conf->oce & OCE_AP) && \ + (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_AP)) + struct wpa_ctrl_dst; struct radius_server_data; struct upnp_wps_device_sm; @@ -53,7 +60,16 @@ struct hapd_interfaces { #ifndef CONFIG_NO_VLAN struct dynamic_iface *vlan_priv; #endif /* CONFIG_NO_VLAN */ +#ifdef CONFIG_ETH_P_OUI + struct dl_list eth_p_oui; /* OUI Extended EtherType handlers */ +#endif /* CONFIG_ETH_P_OUI */ int eloop_initialized; + +#ifdef CONFIG_DPP + int dpp_init_done; + struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */ + struct dl_list dpp_configurator; /* struct dpp_configurator */ +#endif /* CONFIG_DPP */ }; enum hostapd_chan_status { @@ -76,6 +92,7 @@ struct hostapd_rate_data { }; struct hostapd_frame_info { + unsigned int freq; u32 channel; u32 datarate; int ssi_signal; /* dBm */ @@ -109,6 +126,7 @@ struct hostapd_neighbor_entry { struct wpabuf *civic; /* LCI update time */ struct os_time lci_date; + int stationary; }; /** @@ -184,6 +202,17 @@ struct hostapd_data { #endif /* CONFIG_FULL_DYNAMIC_VLAN */ struct l2_packet_data *l2; + +#ifdef CONFIG_IEEE80211R_AP + struct dl_list l2_queue; + struct dl_list l2_oui_queue; + struct eth_p_oui_ctx *oui_pull; + struct eth_p_oui_ctx *oui_resp; + struct eth_p_oui_ctx *oui_push; + struct eth_p_oui_ctx *oui_sreq; + struct eth_p_oui_ctx *oui_sresp; +#endif /* CONFIG_IEEE80211R_AP */ + struct wps_context *wps; int beacon_set_done; @@ -242,9 +271,6 @@ struct hostapd_data { unsigned int cs_c_off_ecsa_beacon; unsigned int cs_c_off_ecsa_proberesp; - /* BSS Load */ - unsigned int bss_load_update_timeout; - #ifdef CONFIG_P2P struct p2p_data *p2p; struct p2p_group *p2p_group; @@ -259,9 +285,6 @@ struct hostapd_data { int noa_start; int noa_duration; #endif /* CONFIG_P2P */ -#ifdef CONFIG_INTERWORKING - size_t gas_frag_limit; -#endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_PROXYARP struct l2_packet_data *sock_dhcp; struct l2_packet_data *sock_ndisc; @@ -292,6 +315,18 @@ struct hostapd_data { unsigned int ext_eapol_frame_io:1; struct l2_packet_data *l2_test; + + enum wpa_alg last_gtk_alg; + int last_gtk_key_idx; + u8 last_gtk[WPA_GTK_MAX_LEN]; + size_t last_gtk_len; + +#ifdef CONFIG_IEEE80211W + enum wpa_alg last_igtk_alg; + int last_igtk_key_idx; + u8 last_igtk[WPA_IGTK_MAX_LEN]; + size_t last_igtk_len; +#endif /* CONFIG_IEEE80211W */ #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_MBO @@ -300,10 +335,42 @@ struct hostapd_data { struct dl_list nr_db; + u8 beacon_req_token; u8 lci_req_token; u8 range_req_token; unsigned int lci_req_active:1; unsigned int range_req_active:1; + + int dhcp_sock; /* UDP socket used with the DHCP server */ + +#ifdef CONFIG_DPP + int dpp_init_done; + struct dpp_authentication *dpp_auth; + u8 dpp_allowed_roles; + int dpp_qr_mutual; + int dpp_auth_ok_on_ack; + int dpp_in_response_listen; + struct gas_query_ap *gas; + struct dpp_pkex *dpp_pkex; + struct dpp_bootstrap_info *dpp_pkex_bi; + char *dpp_pkex_code; + char *dpp_pkex_identifier; + char *dpp_pkex_auth_cmd; + char *dpp_configurator_params; + struct os_reltime dpp_last_init; + struct os_reltime dpp_init_iter_start; + unsigned int dpp_init_max_tries; + unsigned int dpp_init_retry_time; + unsigned int dpp_resp_wait_time; + unsigned int dpp_resp_max_tries; + unsigned int dpp_resp_retry_time; +#ifdef CONFIG_TESTING_OPTIONS + char *dpp_config_obj_override; + char *dpp_discovery_override; + char *dpp_groups_override; + unsigned int dpp_ignore_netaccesskey_mismatch:1; +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_DPP */ }; @@ -311,6 +378,7 @@ struct hostapd_sta_info { struct dl_list list; u8 addr[ETH_ALEN]; struct os_reltime last_seen; + int ssi_signal; #ifdef CONFIG_TAXONOMY struct wpabuf *probe_ie_taxonomy; #endif /* CONFIG_TAXONOMY */ @@ -440,6 +508,10 @@ struct hostapd_iface { u64 last_channel_time_busy; u8 channel_utilization; + unsigned int chan_util_samples_sum; + unsigned int chan_util_num_sample_periods; + unsigned int chan_util_average; + /* eCSA IE will be added only if operating class is specified */ u8 cs_oper_class; @@ -459,6 +531,8 @@ struct hostapd_iface { struct dl_list sta_seen; /* struct hostapd_sta_info */ unsigned int num_sta_seen; + + u8 dfs_domain; }; /* hostapd.c */ @@ -466,6 +540,7 @@ int hostapd_for_each_interface(struct hapd_interfaces *interfaces, int (*cb)(struct hostapd_iface *iface, void *ctx), void *ctx); int hostapd_reload_config(struct hostapd_iface *iface); +void hostapd_reconfig_encryption(struct hostapd_data *hapd); struct hostapd_data * hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, struct hostapd_config *conf, @@ -492,6 +567,7 @@ void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator); void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s); const char * hostapd_state_text(enum hostapd_iface_state s); int hostapd_csa_in_progress(struct hostapd_iface *iface); +void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled); int hostapd_switch_channel(struct hostapd_data *hapd, struct csa_settings *settings); void @@ -499,6 +575,7 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface, const struct hostapd_freq_params *freq_params); void hostapd_cleanup_cs_params(struct hostapd_data *hapd); void hostapd_periodic_iface(struct hostapd_iface *iface); +int hostapd_owe_trans_get_info(struct hostapd_data *hapd); /* utils.c */ int hostapd_register_probereq_cb(struct hostapd_data *hapd, @@ -510,6 +587,8 @@ int hostapd_register_probereq_cb(struct hostapd_data *hapd, void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr); /* drv_callbacks.c (TODO: move to somewhere else?) */ +void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd, + struct sta_info *sta); int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, const u8 *ie, size_t ielen, int reassoc); void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr); @@ -533,6 +612,9 @@ hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces, const char *ifname); +void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr, + enum smps_mode smps_mode, + enum chan_width chan_width, u8 rx_nss); #ifdef CONFIG_FST void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd, diff --git a/src/ap/hs20.c b/src/ap/hs20.c index d7909fad4a14..e265569aef38 100644 --- a/src/ap/hs20.c +++ b/src/ap/hs20.c @@ -11,9 +11,11 @@ #include "common.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" +#include "sta_info.h" #include "hs20.h" @@ -175,3 +177,72 @@ int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd, return ret; } + + +int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd, + const u8 *addr, const char *url) +{ + struct wpabuf *buf; + int ret; + size_t url_len; + + if (!url) { + wpa_printf(MSG_INFO, "HS 2.0: No T&C Server URL available"); + return -1; + } + + url_len = os_strlen(url); + if (5 + url_len > 255) { + wpa_printf(MSG_INFO, + "HS 2.0: Too long T&C Server URL for WNM-Notification: '%s'", + url); + return -1; + } + + buf = wpabuf_alloc(4 + 7 + url_len); + if (!buf) + return -1; + + wpabuf_put_u8(buf, WLAN_ACTION_WNM); + wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ); + wpabuf_put_u8(buf, 1); /* Dialog token */ + wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */ + + /* Terms and Conditions Acceptance subelement */ + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 4 + 1 + url_len); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_WNM_T_C_ACCEPTANCE); + wpabuf_put_u8(buf, url_len); + wpabuf_put_str(buf, url); + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + + wpabuf_free(buf); + + return ret; +} + + +void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta, + int enabled) +{ + if (enabled) { + wpa_printf(MSG_DEBUG, + "HS 2.0: Terms and Conditions filtering required for " + MACSTR, MAC2STR(sta->addr)); + sta->hs20_t_c_filtering = 1; + /* TODO: Enable firewall filtering for the STA */ + wpa_msg(hapd->msg_ctx, MSG_INFO, HS20_T_C_FILTERING_ADD MACSTR, + MAC2STR(sta->addr)); + } else { + wpa_printf(MSG_DEBUG, + "HS 2.0: Terms and Conditions filtering not required for " + MACSTR, MAC2STR(sta->addr)); + sta->hs20_t_c_filtering = 0; + /* TODO: Disable firewall filtering for the STA */ + wpa_msg(hapd->msg_ctx, MSG_INFO, + HS20_T_C_FILTERING_REMOVE MACSTR, MAC2STR(sta->addr)); + } +} diff --git a/src/ap/hs20.h b/src/ap/hs20.h index 152439f4dcb4..e99e26e91158 100644 --- a/src/ap/hs20.h +++ b/src/ap/hs20.h @@ -18,5 +18,9 @@ int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr, int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd, const u8 *addr, const struct wpabuf *payload); +int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd, + const u8 *addr, const char *url); +void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta, + int enabled); #endif /* HS20_H */ diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 16887acdfef4..5279abca1f1f 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -78,10 +78,12 @@ int hostapd_get_hw_features(struct hostapd_iface *iface) int i, j; u16 num_modes, flags; struct hostapd_hw_modes *modes; + u8 dfs_domain; if (hostapd_drv_none(hapd)) return -1; - modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags); + modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags, + &dfs_domain); if (modes == NULL) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -91,6 +93,7 @@ int hostapd_get_hw_features(struct hostapd_iface *iface) } iface->hw_flags = flags; + iface->dfs_domain = dfs_domain; hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); iface->hw_features = modes; @@ -329,6 +332,9 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface) res = ieee80211n_allowed_ht40_channel_pair(iface); if (!res) { iface->conf->secondary_channel = 0; + iface->conf->vht_oper_centr_freq_seg0_idx = 0; + iface->conf->vht_oper_centr_freq_seg1_idx = 0; + iface->conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; res = 1; wpa_printf(MSG_INFO, "Fallback to 20 MHz"); } @@ -621,41 +627,6 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) #ifdef CONFIG_IEEE80211AC - -static int ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, const char *name) -{ - u32 req_cap = conf & cap; - - /* - * Make sure we support all requested capabilities. - * NOTE: We assume that 'cap' represents a capability mask, - * not a discrete value. - */ - if ((hw & req_cap) != req_cap) { - wpa_printf(MSG_ERROR, "Driver does not support configured VHT capability [%s]", - name); - return 0; - } - return 1; -} - - -static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask, - unsigned int shift, - const char *name) -{ - u32 hw_max = hw & mask; - u32 conf_val = conf & mask; - - if (conf_val > hw_max) { - wpa_printf(MSG_ERROR, "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)", - name, conf_val >> shift, hw_max >> shift); - return 0; - } - return 1; -} - - static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface) { struct hostapd_hw_modes *mode = iface->current_mode; @@ -683,45 +654,7 @@ static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface) } } -#define VHT_CAP_CHECK(cap) \ - do { \ - if (!ieee80211ac_cap_check(hw, conf, cap, #cap)) \ - return 0; \ - } while (0) - -#define VHT_CAP_CHECK_MAX(cap) \ - do { \ - if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \ - #cap)) \ - return 0; \ - } while (0) - - VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK); - VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ); - VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ); - VHT_CAP_CHECK(VHT_CAP_RXLDPC); - VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80); - VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160); - VHT_CAP_CHECK(VHT_CAP_TXSTBC); - VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK); - VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE); - VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE); - VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX); - VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX); - VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE); - VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE); - VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS); - VHT_CAP_CHECK(VHT_CAP_HTC_VHT); - VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX); - VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB); - VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB); - VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN); - VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN); - -#undef VHT_CAP_CHECK -#undef VHT_CAP_CHECK_MAX - - return 1; + return ieee80211ac_cap_check(hw, conf); } #endif /* CONFIG_IEEE80211AC */ @@ -746,7 +679,8 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface) if (!ieee80211n_supported_ht_capab(iface)) return -1; #ifdef CONFIG_IEEE80211AC - if (!ieee80211ac_supported_vht_capab(iface)) + if (iface->conf->ieee80211ac && + !ieee80211ac_supported_vht_capab(iface)) return -1; #endif /* CONFIG_IEEE80211AC */ ret = ieee80211n_check_40mhz(iface); @@ -785,20 +719,41 @@ static int hostapd_is_usable_chan(struct hostapd_iface *iface, chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); } + wpa_printf(MSG_INFO, "Channel %d (%s) not allowed for AP mode", + channel, primary ? "primary" : "secondary"); return 0; } static int hostapd_is_usable_chans(struct hostapd_iface *iface) { + int secondary_chan; + if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1)) return 0; if (!iface->conf->secondary_channel) return 1; - return hostapd_is_usable_chan(iface, iface->conf->channel + - iface->conf->secondary_channel * 4, 0); + if (!iface->conf->ht40_plus_minus_allowed) + return hostapd_is_usable_chan( + iface, iface->conf->channel + + iface->conf->secondary_channel * 4, 0); + + /* Both HT40+ and HT40- are set, pick a valid secondary channel */ + secondary_chan = iface->conf->channel + 4; + if (hostapd_is_usable_chan(iface, secondary_chan, 0)) { + iface->conf->secondary_channel = 1; + return 1; + } + + secondary_chan = iface->conf->channel - 4; + if (hostapd_is_usable_chan(iface, secondary_chan, 0)) { + iface->conf->secondary_channel = -1; + return 1; + } + + return 0; } @@ -978,5 +933,19 @@ int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) { - return hw_get_chan(hapd->iface->current_mode, freq); + int i, channel; + struct hostapd_hw_modes *mode; + + channel = hw_get_chan(hapd->iface->current_mode, freq); + if (channel) + return channel; + /* Check other available modes since the channel list for the current + * mode did not include the specified frequency. */ + for (i = 0; i < hapd->iface->num_hw_features; i++) { + mode = &hapd->iface->hw_features[i]; + channel = hw_get_chan(mode, freq); + if (channel) + return channel; + } + return 0; } diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 333035fe7703..f9bb99d98549 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -1,6 +1,6 @@ /* * hostapd / IEEE 802.11 Management - * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,6 +14,8 @@ #include "utils/eloop.h" #include "crypto/crypto.h" #include "crypto/sha256.h" +#include "crypto/sha384.h" +#include "crypto/sha512.h" #include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" @@ -45,8 +47,21 @@ #include "mbo_ap.h" #include "rrm.h" #include "taxonomy.h" +#include "fils_hlp.h" +#include "dpp_hostapd.h" +#include "gas_query_ap.h" +#ifdef CONFIG_FILS +static struct wpabuf * +prepare_auth_resp_fils(struct hostapd_data *hapd, + struct sta_info *sta, u16 *resp, + struct rsn_pmksa_cache_entry *pmksa, + struct wpabuf *erp_resp, + const u8 *msk, size_t msk_len, + int *is_pub); +#endif /* CONFIG_FILS */ + u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; @@ -262,7 +277,7 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, 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) + const u8 *ies, size_t ies_len, const char *dbg) { struct ieee80211_mgmt *reply; u8 *buf; @@ -289,9 +304,9 @@ static int send_auth_reply(struct hostapd_data *hapd, 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)", + " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu) (dbg=%s)", MAC2STR(dst), auth_alg, auth_transaction, - resp, (unsigned long) ies_len); + resp, (unsigned long) ies_len, dbg); if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0) wpa_printf(MSG_INFO, "send_auth_reply: send failed"); else @@ -303,7 +318,7 @@ static int send_auth_reply(struct hostapd_data *hapd, } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP 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) @@ -313,7 +328,8 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, int reply_res; reply_res = send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, - auth_transaction, status, ies, ies_len); + auth_transaction, status, ies, ies_len, + "auth-ft-finish"); sta = ap_get_sta(hapd, dst); if (sta == NULL) @@ -334,38 +350,65 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, sta->flags |= WLAN_STA_AUTH; mlme_authenticate_indication(hapd, sta); } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE -#define dot11RSNASAESync 5 /* attempts */ +static void sae_set_state(struct sta_info *sta, enum sae_state state, + const char *reason) +{ + wpa_printf(MSG_DEBUG, "SAE: State %s -> %s for peer " MACSTR " (%s)", + sae_state_txt(sta->sae->state), sae_state_txt(state), + MAC2STR(sta->addr), reason); + sta->sae->state = state; +} static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, struct sta_info *sta, int update) { struct wpabuf *buf; + const char *password = NULL; + struct sae_password_entry *pw; + const char *rx_id = NULL; + + if (sta->sae->tmp) + rx_id = sta->sae->tmp->pw_id; - if (hapd->conf->ssid.wpa_passphrase == NULL) { + for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) { + if (!is_broadcast_ether_addr(pw->peer_addr) && + os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0) + continue; + if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier)) + continue; + if (rx_id && pw->identifier && + os_strcmp(rx_id, pw->identifier) != 0) + continue; + password = pw->password; + break; + } + if (!password) + password = hapd->conf->ssid.wpa_passphrase; + if (!password) { 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), + (u8 *) password, os_strlen(password), rx_id, sta->sae) < 0) { wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE"); return NULL; } - buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN); + buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN + + (rx_id ? 3 + os_strlen(rx_id) : 0)); if (buf == NULL) return NULL; sae_write_commit(sta->sae, buf, sta->sae->tmp ? - sta->sae->tmp->anti_clogging_token : NULL); + sta->sae->tmp->anti_clogging_token : NULL, rx_id); return buf; } @@ -394,12 +437,14 @@ static int auth_sae_send_commit(struct hostapd_data *hapd, int reply_res; data = auth_build_sae_commit(hapd, sta, update); + if (!data && sta->sae->tmp && sta->sae->tmp->pw_id) + return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; 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_len(data), "sae-send-commit"); wpabuf_free(data); @@ -420,7 +465,7 @@ static int auth_sae_send_confirm(struct hostapd_data *hapd, reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS, wpabuf_head(data), - wpabuf_len(data)); + wpabuf_len(data), "sae-send-confirm"); wpabuf_free(data); @@ -499,10 +544,10 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, } -static int sae_check_big_sync(struct sta_info *sta) +static int sae_check_big_sync(struct hostapd_data *hapd, struct sta_info *sta) { - if (sta->sae->sync > dot11RSNASAESync) { - sta->sae->state = SAE_NOTHING; + if (sta->sae->sync > hapd->conf->sae_sync) { + sae_set_state(sta, SAE_NOTHING, "Sync > dot11RSNASAESync"); sta->sae->sync = 0; return -1; } @@ -516,12 +561,13 @@ static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data) struct sta_info *sta = eloop_data; int ret; - if (sae_check_big_sync(sta)) + if (sae_check_big_sync(hapd, 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); + " (sync=%d state=%s)", + MAC2STR(sta->addr), sta->sae->sync, + sae_state_txt(sta->sae->state)); switch (sta->sae->state) { case SAE_COMMITTED: @@ -570,7 +616,7 @@ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) 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; + sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm"); wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, sta->sae->pmk, sta->sae->pmkid); } @@ -584,13 +630,16 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, if (auth_transaction != 1 && auth_transaction != 2) return WLAN_STATUS_UNSPECIFIED_FAILURE; + wpa_printf(MSG_DEBUG, "SAE: Peer " MACSTR " state=%s auth_trans=%u", + MAC2STR(sta->addr), sae_state_txt(sta->sae->state), + auth_transaction); 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; + sae_set_state(sta, SAE_COMMITTED, "Sent Commit"); if (sae_process_commit(sta->sae) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -612,7 +661,8 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, ret = auth_sae_send_confirm(hapd, sta, bssid); if (ret) return ret; - sta->sae->state = SAE_CONFIRMED; + sae_set_state(sta, SAE_CONFIRMED, + "Sent Confirm (mesh)"); } else { /* * For infrastructure BSS, send only the Commit @@ -641,7 +691,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, ret = auth_sae_send_confirm(hapd, sta, bssid); if (ret) return ret; - sta->sae->state = SAE_CONFIRMED; + sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm"); sta->sae->sync = 0; sae_set_retransmit_timer(hapd, sta); } else if (hapd->conf->mesh & MESH_ENABLED) { @@ -649,7 +699,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, * In mesh case, follow SAE finite state machine and * send Commit now, if sync count allows. */ - if (sae_check_big_sync(sta)) + if (sae_check_big_sync(hapd, sta)) return WLAN_STATUS_SUCCESS; sta->sae->sync++; @@ -668,7 +718,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, if (ret) return ret; - sta->sae->state = SAE_CONFIRMED; + sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm"); /* * Since this was triggered on Confirm RX, run another @@ -681,7 +731,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, case SAE_CONFIRMED: sae_clear_retransmit_timer(hapd, sta); if (auth_transaction == 1) { - if (sae_check_big_sync(sta)) + if (sae_check_big_sync(hapd, sta)) return WLAN_STATUS_SUCCESS; sta->sae->sync++; @@ -698,18 +748,31 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, sae_set_retransmit_timer(hapd, sta); } else { + sta->sae->send_confirm = 0xffff; sae_accept_sta(hapd, sta); } break; case SAE_ACCEPTED: - if (auth_transaction == 1) { + if (auth_transaction == 1 && + (hapd->conf->mesh & MESH_ENABLED)) { 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 (auth_transaction == 1) { + wpa_printf(MSG_DEBUG, "SAE: Start reauthentication"); + ret = auth_sae_send_commit(hapd, sta, bssid, 1); + if (ret) + return ret; + sae_set_state(sta, SAE_COMMITTED, "Sent Commit"); + + if (sae_process_commit(sta->sae) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + sta->sae->sync = 0; + sae_set_retransmit_timer(hapd, sta); } else { - if (sae_check_big_sync(sta)) + if (sae_check_big_sync(hapd, sta)) return WLAN_STATUS_SUCCESS; sta->sae->sync++; @@ -773,6 +836,29 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, int resp = WLAN_STATUS_SUCCESS; struct wpabuf *data = NULL; +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->conf->sae_reflection_attack && auth_transaction == 1) { + const u8 *pos, *end; + + wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack"); + pos = mgmt->u.auth.variable; + end = ((const u8 *) mgmt) + len; + send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, + auth_transaction, resp, pos, end - pos, + "auth-sae-reflection-attack"); + goto remove_sta; + } + + if (hapd->conf->sae_commit_override && auth_transaction == 1) { + wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override"); + send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, + auth_transaction, resp, + wpabuf_head(hapd->conf->sae_commit_override), + wpabuf_len(hapd->conf->sae_commit_override), + "sae-commit-override"); + goto remove_sta; + } +#endif /* CONFIG_TESTING_OPTIONS */ if (!sta->sae) { if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS) { @@ -784,7 +870,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, resp = -1; goto remove_sta; } - sta->sae->state = SAE_NOTHING; + sae_set_state(sta, SAE_NOTHING, "Init"); sta->sae->sync = 0; } @@ -847,7 +933,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, "SAE: Failed to send commit message"); goto remove_sta; } - sta->sae->state = SAE_COMMITTED; + sae_set_state(sta, SAE_COMMITTED, + "Sent Commit (anti-clogging token case in mesh)"); sta->sae->sync = 0; sae_set_retransmit_timer(hapd, sta); return; @@ -866,6 +953,20 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, if (status_code != WLAN_STATUS_SUCCESS) goto remove_sta; + if (!(hapd->conf->mesh & MESH_ENABLED) && + sta->sae->state == SAE_COMMITTED) { + /* This is needed in the infrastructure BSS case to + * address a sequence where a STA entry may remain in + * hostapd across two attempts to do SAE authentication + * by the same STA. The second attempt may end up trying + * to use a different group and that would not be + * allowed if we remain in Committed state with the + * previously set parameters. */ + sae_set_state(sta, SAE_NOTHING, + "Clear existing state to allow restart"); + sae_clear_data(sta->sae); + } + resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable, ((const u8 *) mgmt) + len - mgmt->u.auth.variable, &token, @@ -876,6 +977,17 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, MAC2STR(sta->addr)); goto remove_sta; } + + if (resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) { + wpa_msg(hapd->msg_ctx, MSG_INFO, + WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER + MACSTR, MAC2STR(sta->addr)); + sae_clear_retransmit_timer(hapd, sta); + sae_set_state(sta, SAE_NOTHING, + "Unknown Password Identifier"); + goto remove_sta; + } + if (token && check_sae_token(hapd, sta->addr, token, token_len) < 0) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message with " @@ -896,7 +1008,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, sta->addr); resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ; if (hapd->conf->mesh & MESH_ENABLED) - sta->sae->state = SAE_NOTHING; + sae_set_state(sta, SAE_NOTHING, + "Request anti-clogging token case in mesh"); goto reply; } @@ -910,12 +1023,36 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, 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) { + const u8 *var; + size_t var_len; + u16 peer_send_confirm; + + var = mgmt->u.auth.variable; + var_len = ((u8 *) mgmt) + len - mgmt->u.auth.variable; + if (var_len < 2) { resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto reply; } + + peer_send_confirm = WPA_GET_LE16(var); + + if (sta->sae->state == SAE_ACCEPTED && + (peer_send_confirm <= sta->sae->rc || + peer_send_confirm == 0xffff)) { + wpa_printf(MSG_DEBUG, + "SAE: Silently ignore unexpected Confirm from peer " + MACSTR + " (peer-send-confirm=%u Rc=%u)", + MAC2STR(sta->addr), + peer_send_confirm, sta->sae->rc); + return; + } + + if (sae_check_confirm(sta->sae, var, var_len) < 0) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto reply; + } + sta->sae->rc = peer_send_confirm; } resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction); } else { @@ -933,7 +1070,7 @@ reply: send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, auth_transaction, resp, data ? wpabuf_head(data) : (u8 *) "", - data ? wpabuf_len(data) : 0); + data ? wpabuf_len(data) : 0, "auth-sae"); } remove_sta: @@ -970,7 +1107,7 @@ int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta) if (ret) return -1; - sta->sae->state = SAE_COMMITTED; + sae_set_state(sta, SAE_COMMITTED, "Init and sent commit"); sta->sae->sync = 0; sae_set_retransmit_timer(hapd, sta); @@ -980,6 +1117,631 @@ int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta) #endif /* CONFIG_SAE */ +static u16 wpa_res_to_status_code(int res) +{ + if (res == WPA_INVALID_GROUP) + return WLAN_STATUS_GROUP_CIPHER_NOT_VALID; + if (res == WPA_INVALID_PAIRWISE) + return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + if (res == WPA_INVALID_AKMP) + return WLAN_STATUS_AKMP_NOT_VALID; + if (res == WPA_ALLOC_FAIL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; +#ifdef CONFIG_IEEE80211W + if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) + return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; + if (res == WPA_INVALID_MGMT_GROUP_CIPHER) + return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; +#endif /* CONFIG_IEEE80211W */ + if (res == WPA_INVALID_MDIE) + return WLAN_STATUS_INVALID_MDIE; + if (res == WPA_INVALID_PMKID) + return WLAN_STATUS_INVALID_PMKID; + if (res != WPA_IE_OK) + return WLAN_STATUS_INVALID_IE; + return WLAN_STATUS_SUCCESS; +} + + +#ifdef CONFIG_FILS + +static void handle_auth_fils_finish(struct hostapd_data *hapd, + struct sta_info *sta, u16 resp, + struct wpabuf *data, int pub); + +void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *pos, size_t len, u16 auth_alg, + u16 auth_transaction, u16 status_code, + void (*cb)(struct hostapd_data *hapd, + struct sta_info *sta, u16 resp, + struct wpabuf *data, int pub)) +{ + u16 resp = WLAN_STATUS_SUCCESS; + const u8 *end; + struct ieee802_11_elems elems; + int res; + struct wpa_ie_data rsn; + struct rsn_pmksa_cache_entry *pmksa = NULL; + + if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS) + return; + + end = pos + len; + + wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields", + pos, end - pos); + + /* TODO: FILS PK */ +#ifdef CONFIG_FILS_SK_PFS + if (auth_alg == WLAN_AUTH_FILS_SK_PFS) { + u16 group; + struct wpabuf *pub; + size_t elem_len; + + /* Using FILS PFS */ + + /* Finite Cyclic Group */ + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, + "FILS: No room for Finite Cyclic Group"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + group = WPA_GET_LE16(pos); + pos += 2; + if (group != hapd->conf->fils_dh_group) { + wpa_printf(MSG_DEBUG, + "FILS: Unsupported Finite Cyclic Group: %u (expected %u)", + group, hapd->conf->fils_dh_group); + resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + goto fail; + } + + crypto_ecdh_deinit(sta->fils_ecdh); + sta->fils_ecdh = crypto_ecdh_init(group); + if (!sta->fils_ecdh) { + wpa_printf(MSG_INFO, + "FILS: Could not initialize ECDH with group %d", + group); + resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + goto fail; + } + + pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1); + if (!pub) { + wpa_printf(MSG_DEBUG, + "FILS: Failed to derive ECDH public key"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + elem_len = wpabuf_len(pub); + wpabuf_free(pub); + + /* Element */ + if ((size_t) (end - pos) < elem_len) { + wpa_printf(MSG_DEBUG, "FILS: No room for Element"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + wpabuf_free(sta->fils_g_sta); + sta->fils_g_sta = wpabuf_alloc_copy(pos, elem_len); + wpabuf_clear_free(sta->fils_dh_ss); + sta->fils_dh_ss = crypto_ecdh_set_peerkey(sta->fils_ecdh, 1, + pos, elem_len); + if (!sta->fils_dh_ss) { + wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", sta->fils_dh_ss); + pos += elem_len; + } else { + crypto_ecdh_deinit(sta->fils_ecdh); + sta->fils_ecdh = NULL; + wpabuf_clear_free(sta->fils_dh_ss); + sta->fils_dh_ss = NULL; + } +#endif /* CONFIG_FILS_SK_PFS */ + + wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos); + if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, "FILS: Could not parse elements"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + /* RSNE */ + wpa_hexdump(MSG_DEBUG, "FILS: RSN element", + elems.rsn_ie, elems.rsn_ie_len); + if (!elems.rsn_ie || + wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsn) < 0) { + wpa_printf(MSG_DEBUG, "FILS: No valid RSN element"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (!sta->wpa_sm) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, + NULL); + if (!sta->wpa_sm) { + wpa_printf(MSG_DEBUG, + "FILS: Failed to initialize RSN state machine"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + elems.rsn_ie - 2, elems.rsn_ie_len + 2, + elems.mdie, elems.mdie_len, NULL, 0); + resp = wpa_res_to_status_code(res); + if (resp != WLAN_STATUS_SUCCESS) + goto fail; + + if (!elems.fils_nonce) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.fils_nonce, + FILS_NONCE_LEN); + os_memcpy(sta->fils_snonce, elems.fils_nonce, FILS_NONCE_LEN); + + /* PMKID List */ + if (rsn.pmkid && rsn.num_pmkid > 0) { + u8 num; + const u8 *pmkid; + + wpa_hexdump(MSG_DEBUG, "FILS: PMKID List", + rsn.pmkid, rsn.num_pmkid * PMKID_LEN); + + pmkid = rsn.pmkid; + num = rsn.num_pmkid; + while (num) { + wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN); + pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, + pmkid); + if (pmksa) + break; + pmksa = wpa_auth_pmksa_get_fils_cache_id(hapd->wpa_auth, + sta->addr, + pmkid); + if (pmksa) + break; + pmkid += PMKID_LEN; + num--; + } + } + if (pmksa && wpa_auth_sta_key_mgmt(sta->wpa_sm) != pmksa->akmp) { + wpa_printf(MSG_DEBUG, + "FILS: Matching PMKSA cache entry has different AKMP (0x%x != 0x%x) - ignore", + wpa_auth_sta_key_mgmt(sta->wpa_sm), pmksa->akmp); + pmksa = NULL; + } + if (pmksa) + wpa_printf(MSG_DEBUG, "FILS: Found matching PMKSA cache entry"); + + /* FILS Session */ + if (!elems.fils_session) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Session element"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session, + FILS_SESSION_LEN); + os_memcpy(sta->fils_session, elems.fils_session, FILS_SESSION_LEN); + + /* FILS Wrapped Data */ + if (elems.fils_wrapped_data) { + wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data", + elems.fils_wrapped_data, + elems.fils_wrapped_data_len); + if (!pmksa) { +#ifndef CONFIG_NO_RADIUS + if (!sta->eapol_sm) { + sta->eapol_sm = + ieee802_1x_alloc_eapol_sm(hapd, sta); + } + wpa_printf(MSG_DEBUG, + "FILS: Forward EAP-Initiate/Re-auth to authentication server"); + ieee802_1x_encapsulate_radius( + hapd, sta, elems.fils_wrapped_data, + elems.fils_wrapped_data_len); + sta->fils_pending_cb = cb; + wpa_printf(MSG_DEBUG, + "FILS: Will send Authentication frame once the response from authentication server is available"); + sta->flags |= WLAN_STA_PENDING_FILS_ERP; + /* Calculate pending PMKID here so that we do not need + * to maintain a copy of the EAP-Initiate/Reauth + * message. */ + if (fils_pmkid_erp(wpa_auth_sta_key_mgmt(sta->wpa_sm), + elems.fils_wrapped_data, + elems.fils_wrapped_data_len, + sta->fils_erp_pmkid) == 0) + sta->fils_erp_pmkid_set = 1; + return; +#else /* CONFIG_NO_RADIUS */ + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; +#endif /* CONFIG_NO_RADIUS */ + } + } + +fail: + if (cb) { + struct wpabuf *data; + int pub = 0; + + data = prepare_auth_resp_fils(hapd, sta, &resp, pmksa, NULL, + NULL, 0, &pub); + if (!data) { + wpa_printf(MSG_DEBUG, + "%s: prepare_auth_resp_fils() returned failure", + __func__); + } + + cb(hapd, sta, resp, data, pub); + } +} + + +static struct wpabuf * +prepare_auth_resp_fils(struct hostapd_data *hapd, + struct sta_info *sta, u16 *resp, + struct rsn_pmksa_cache_entry *pmksa, + struct wpabuf *erp_resp, + const u8 *msk, size_t msk_len, + int *is_pub) +{ + u8 fils_nonce[FILS_NONCE_LEN]; + size_t ielen; + struct wpabuf *data = NULL; + const u8 *ie; + u8 *ie_buf = NULL; + const u8 *pmk = NULL; + size_t pmk_len = 0; + u8 pmk_buf[PMK_LEN_MAX]; + struct wpabuf *pub = NULL; + + if (*resp != WLAN_STATUS_SUCCESS) + goto fail; + + ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen); + if (!ie) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (pmksa) { + /* Add PMKID of the selected PMKSA into RSNE */ + ie_buf = os_malloc(ielen + 2 + 2 + PMKID_LEN); + if (!ie_buf) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + os_memcpy(ie_buf, ie, ielen); + if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid) < 0) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + ie = ie_buf; + } + + if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce", + fils_nonce, FILS_NONCE_LEN); + +#ifdef CONFIG_FILS_SK_PFS + if (sta->fils_dh_ss && sta->fils_ecdh) { + pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1); + if (!pub) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + } +#endif /* CONFIG_FILS_SK_PFS */ + + data = wpabuf_alloc(1000 + ielen + (pub ? wpabuf_len(pub) : 0)); + if (!data) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + /* TODO: FILS PK */ +#ifdef CONFIG_FILS_SK_PFS + if (pub) { + /* Finite Cyclic Group */ + wpabuf_put_le16(data, hapd->conf->fils_dh_group); + + /* Element */ + wpabuf_put_buf(data, pub); + } +#endif /* CONFIG_FILS_SK_PFS */ + + /* RSNE */ + wpabuf_put_data(data, ie, ielen); + + /* MDE when using FILS+FT (already included in ie,ielen with RSNE) */ + +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm))) { + /* FTE[R1KH-ID,R0KH-ID] when using FILS+FT */ + int res; + int use_sha384 = wpa_key_mgmt_sha384( + wpa_auth_sta_key_mgmt(sta->wpa_sm)); + + res = wpa_auth_write_fte(hapd->wpa_auth, use_sha384, + wpabuf_put(data, 0), + wpabuf_tailroom(data)); + if (res < 0) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpabuf_put(data, res); + } +#endif /* CONFIG_IEEE80211R_AP */ + + /* FILS Nonce */ + wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(data, 1 + FILS_NONCE_LEN); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(data, WLAN_EID_EXT_FILS_NONCE); + wpabuf_put_data(data, fils_nonce, FILS_NONCE_LEN); + + /* FILS Session */ + wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(data, 1 + FILS_SESSION_LEN); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(data, WLAN_EID_EXT_FILS_SESSION); + wpabuf_put_data(data, sta->fils_session, FILS_SESSION_LEN); + + /* FILS Wrapped Data */ + if (!pmksa && erp_resp) { + wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(data, 1 + wpabuf_len(erp_resp)); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(data, WLAN_EID_EXT_FILS_WRAPPED_DATA); + wpabuf_put_buf(data, erp_resp); + + if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm), + msk, msk_len, sta->fils_snonce, fils_nonce, + sta->fils_dh_ss ? + wpabuf_head(sta->fils_dh_ss) : NULL, + sta->fils_dh_ss ? + wpabuf_len(sta->fils_dh_ss) : 0, + pmk_buf, &pmk_len)) { + wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK"); + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + wpabuf_free(data); + data = NULL; + goto fail; + } + pmk = pmk_buf; + + /* Don't use DHss in PTK derivation if PMKSA caching is not + * used. */ + wpabuf_clear_free(sta->fils_dh_ss); + sta->fils_dh_ss = NULL; + + if (sta->fils_erp_pmkid_set) { + /* TODO: get PMKLifetime from WPA parameters */ + unsigned int dot11RSNAConfigPMKLifetime = 43200; + int session_timeout; + + session_timeout = dot11RSNAConfigPMKLifetime; + if (sta->session_timeout_set) { + struct os_reltime now, diff; + + os_get_reltime(&now); + os_reltime_sub(&sta->session_timeout, &now, + &diff); + session_timeout = diff.sec; + } + + sta->fils_erp_pmkid_set = 0; + if (wpa_auth_pmksa_add2( + hapd->wpa_auth, sta->addr, + pmk, pmk_len, + sta->fils_erp_pmkid, + session_timeout, + wpa_auth_sta_key_mgmt(sta->wpa_sm)) < 0) { + wpa_printf(MSG_ERROR, + "FILS: Failed to add PMKSA cache entry based on ERP"); + } + } + } else if (pmksa) { + pmk = pmksa->pmk; + pmk_len = pmksa->pmk_len; + } + + if (!pmk) { + wpa_printf(MSG_DEBUG, "FILS: No PMK available"); + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + wpabuf_free(data); + data = NULL; + goto fail; + } + + if (fils_auth_pmk_to_ptk(sta->wpa_sm, pmk, pmk_len, + sta->fils_snonce, fils_nonce, + sta->fils_dh_ss ? + wpabuf_head(sta->fils_dh_ss) : NULL, + sta->fils_dh_ss ? + wpabuf_len(sta->fils_dh_ss) : 0, + sta->fils_g_sta, pub) < 0) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + wpabuf_free(data); + data = NULL; + goto fail; + } + +fail: + if (is_pub) + *is_pub = pub != NULL; + os_free(ie_buf); + wpabuf_free(pub); + wpabuf_clear_free(sta->fils_dh_ss); + sta->fils_dh_ss = NULL; +#ifdef CONFIG_FILS_SK_PFS + crypto_ecdh_deinit(sta->fils_ecdh); + sta->fils_ecdh = NULL; +#endif /* CONFIG_FILS_SK_PFS */ + return data; +} + + +static void handle_auth_fils_finish(struct hostapd_data *hapd, + struct sta_info *sta, u16 resp, + struct wpabuf *data, int pub) +{ + u16 auth_alg; + + auth_alg = (pub || + resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ? + WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK; + send_auth_reply(hapd, sta->addr, hapd->own_addr, auth_alg, 2, resp, + data ? wpabuf_head(data) : (u8 *) "", + data ? wpabuf_len(data) : 0, "auth-fils-finish"); + wpabuf_free(data); + + if (resp == WLAN_STATUS_SUCCESS) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "authentication OK (FILS)"); + sta->flags |= WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->auth_alg = pub ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK; + mlme_authenticate_indication(hapd, sta); + } +} + + +void ieee802_11_finish_fils_auth(struct hostapd_data *hapd, + struct sta_info *sta, int success, + struct wpabuf *erp_resp, + const u8 *msk, size_t msk_len) +{ + struct wpabuf *data; + int pub = 0; + u16 resp; + + sta->flags &= ~WLAN_STA_PENDING_FILS_ERP; + + if (!sta->fils_pending_cb) + return; + resp = success ? WLAN_STATUS_SUCCESS : WLAN_STATUS_UNSPECIFIED_FAILURE; + data = prepare_auth_resp_fils(hapd, sta, &resp, NULL, erp_resp, + msk, msk_len, &pub); + if (!data) { + wpa_printf(MSG_DEBUG, + "%s: prepare_auth_resp_fils() returned failure", + __func__); + } + sta->fils_pending_cb(hapd, sta, resp, data, pub); +} + +#endif /* CONFIG_FILS */ + + +int +ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr, + const u8 *msg, size_t len, u32 *session_timeout, + u32 *acct_interim_interval, + struct vlan_description *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui, int is_probe_req) +{ + int res; + + os_memset(vlan_id, 0, sizeof(*vlan_id)); + res = hostapd_allowed_address(hapd, addr, msg, len, + session_timeout, acct_interim_interval, + vlan_id, psk, identity, radius_cui, + is_probe_req); + + if (res == HOSTAPD_ACL_REJECT) { + if (!is_probe_req) + wpa_printf(MSG_DEBUG, + "Station " MACSTR + " not allowed to authenticate", + MAC2STR(addr)); + return HOSTAPD_ACL_REJECT; + } + + if (res == HOSTAPD_ACL_PENDING) { + wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR + " waiting for an external authentication", + MAC2STR(addr)); + /* Authentication code will re-send the authentication frame + * after it has received (and cached) information from the + * external source. */ + return HOSTAPD_ACL_PENDING; + } + + return res; +} + + +static int +ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta, + int res, u32 session_timeout, + u32 acct_interim_interval, + struct vlan_description *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui) +{ + 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] ? "+" : ""); + return -1; + } + if (ap_sta_set_vlan(hapd, sta, vlan_id) < 0) + return -1; + 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; + } + + os_free(sta->identity); + sta->identity = *identity; + *identity = NULL; + + os_free(sta->radius_cui); + sta->radius_cui = *radius_cui; + *radius_cui = NULL; + + if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval) + sta->acct_interim_interval = acct_interim_interval; + if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) { + sta->session_timeout_set = 1; + os_get_reltime(&sta->session_timeout); + sta->session_timeout.sec += session_timeout; + ap_sta_session_timeout(hapd, sta, session_timeout); + } else { + sta->session_timeout_set = 0; + ap_sta_no_session_timeout(hapd, sta); + } + + return 0; +} + + static void handle_auth(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { @@ -998,8 +1760,6 @@ static void handle_auth(struct hostapd_data *hapd, 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); @@ -1047,20 +1807,29 @@ static void handle_auth(struct hostapd_data *hapd, #endif /* CONFIG_NO_RC4 */ if (hapd->tkip_countermeasures) { - resp = WLAN_REASON_MICHAEL_MIC_FAILURE; + wpa_printf(MSG_DEBUG, + "Ongoing TKIP countermeasures (Michael MIC failure) - reject authentication"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) && auth_alg == WLAN_AUTH_OPEN) || -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && auth_alg == WLAN_AUTH_FT) || -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && auth_alg == WLAN_AUTH_SAE) || #endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) && + auth_alg == WLAN_AUTH_FILS_SK) || + (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) && + hapd->conf->fils_dh_group && + auth_alg == WLAN_AUTH_FILS_SK_PFS) || +#endif /* CONFIG_FILS */ ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && auth_alg == WLAN_AUTH_SHARED_KEY))) { wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)", @@ -1139,29 +1908,23 @@ static void handle_auth(struct hostapd_data *hapd, } } - res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len, - &session_timeout, - &acct_interim_interval, &vlan_id, - &psk, &identity, &radius_cui); - + res = ieee802_11_allowed_address( + hapd, mgmt->sa, (const u8 *) mgmt, len, &session_timeout, + &acct_interim_interval, &vlan_id, &psk, &identity, &radius_cui, + 0); if (res == HOSTAPD_ACL_REJECT) { - wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate", - MAC2STR(mgmt->sa)); + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Ignore Authentication frame from " MACSTR + " due to ACL reject", 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. */ + if (res == HOSTAPD_ACL_PENDING) return; - } sta = ap_get_sta(hapd, mgmt->sa); if (sta) { + sta->flags &= ~WLAN_STA_PENDING_FILS_ERP; if ((fc & WLAN_FC_RETRY) && sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && sta->last_seq_ctrl == seq_ctrl && @@ -1203,6 +1966,7 @@ static void handle_auth(struct hostapd_data *hapd, sta = ap_sta_add(hapd, mgmt->sa); if (!sta) { + wpa_printf(MSG_DEBUG, "ap_sta_add() failed"); resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } @@ -1210,47 +1974,18 @@ static void handle_auth(struct hostapd_data *hapd, 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] ? "+" : ""); + res = ieee802_11_set_radius_info( + hapd, sta, res, session_timeout, acct_interim_interval, + &vlan_id, &psk, &identity, &radius_cui); + if (res) { + wpa_printf(MSG_DEBUG, "ieee802_11_set_radius_info() failed"); 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 @@ -1263,8 +1998,15 @@ static void handle_auth(struct hostapd_data *hapd, * * In mesh mode, the station was already added to the driver when the * NEW_PEER_CANDIDATE event is received. + * + * If PMF was negotiated for the existing association, skip this to + * avoid dropping the STA entry and the associated keys. This is needed + * to allow the original connection work until the attempt can complete + * (re)association, so that unprotected Authentication frame cannot be + * used to bypass PMF protection. */ if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) && + (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) && !(hapd->conf->mesh & MESH_ENABLED) && !(sta->added_unassoc)) { /* @@ -1274,6 +2016,7 @@ static void handle_auth(struct hostapd_data *hapd, * updated. To handle this, station's added_unassoc flag is * cleared once the station has completed association. */ + ap_sta_set_authorized(hapd, sta, 0); hostapd_drv_sta_remove(hapd, sta->addr); sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH | WLAN_STA_AUTHORIZED); @@ -1305,6 +2048,9 @@ static void handle_auth(struct hostapd_data *hapd, case WLAN_AUTH_SHARED_KEY: resp = auth_shared_key(hapd, sta, auth_transaction, challenge, fc & WLAN_FC_ISWEP); + if (resp != 0) + wpa_printf(MSG_DEBUG, + "auth_shared_key() failed: status=%d", resp); sta->auth_alg = WLAN_AUTH_SHARED_KEY; mlme_authenticate_indication(hapd, sta); if (sta->challenge && auth_transaction == 1) { @@ -1316,7 +2062,7 @@ static void handle_auth(struct hostapd_data *hapd, } break; #endif /* CONFIG_NO_RC4 */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP case WLAN_AUTH_FT: sta->auth_alg = WLAN_AUTH_FT; if (sta->wpa_sm == NULL) @@ -1335,7 +2081,7 @@ static void handle_auth(struct hostapd_data *hapd, handle_auth_ft_finish, hapd); /* handle_auth_ft_finish() callback will complete auth. */ return; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE case WLAN_AUTH_SAE: #ifdef CONFIG_MESH @@ -1357,6 +2103,15 @@ static void handle_auth(struct hostapd_data *hapd, status_code); return; #endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + case WLAN_AUTH_FILS_SK: + case WLAN_AUTH_FILS_SK_PFS: + handle_auth_fils(hapd, sta, mgmt->u.auth.variable, + len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth), + auth_alg, auth_transaction, status_code, + handle_auth_fils_finish); + return; +#endif /* CONFIG_FILS */ } fail: @@ -1366,7 +2121,7 @@ static void handle_auth(struct hostapd_data *hapd, reply_res = send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, auth_transaction + 1, resp, resp_ies, - resp_ies_len); + resp_ies_len, "handle-auth"); if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS || reply_res != WLAN_STATUS_SUCCESS)) { @@ -1459,6 +2214,11 @@ static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta, static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta, struct ieee802_11_elems *elems) { + /* Supported rates not used in IEEE 802.11ad/DMG */ + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) + return WLAN_STATUS_SUCCESS; + if (!elems->supp_rates) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -1496,13 +2256,188 @@ static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_INTERWORKING */ - if (ext_capab_ie_len > 0) + if (ext_capab_ie_len > 0) { sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2)); + os_free(sta->ext_capability); + sta->ext_capability = os_malloc(1 + ext_capab_ie_len); + if (sta->ext_capability) { + sta->ext_capability[0] = ext_capab_ie_len; + os_memcpy(sta->ext_capability + 1, ext_capab_ie, + ext_capab_ie_len); + } + } return WLAN_STATUS_SUCCESS; } +#ifdef CONFIG_OWE + +static int owe_group_supported(struct hostapd_data *hapd, u16 group) +{ + int i; + int *groups = hapd->conf->owe_groups; + + if (group != 19 && group != 20 && group != 21) + return 0; + + if (!groups) + return 1; + + for (i = 0; groups[i] > 0; i++) { + if (groups[i] == group) + return 1; + } + + return 0; +} + + +static u16 owe_process_assoc_req(struct hostapd_data *hapd, + struct sta_info *sta, const u8 *owe_dh, + u8 owe_dh_len) +{ + struct wpabuf *secret, *pub, *hkey; + int res; + u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN]; + const char *info = "OWE Key Generation"; + const u8 *addr[2]; + size_t len[2]; + u16 group; + size_t hash_len, prime_len; + + if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) { + wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching"); + return WLAN_STATUS_SUCCESS; + } + + group = WPA_GET_LE16(owe_dh); + if (!owe_group_supported(hapd, group)) { + wpa_printf(MSG_DEBUG, "OWE: Unsupported DH group %u", group); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + if (group == 19) + prime_len = 32; + else if (group == 20) + prime_len = 48; + else if (group == 21) + prime_len = 66; + else + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + + crypto_ecdh_deinit(sta->owe_ecdh); + sta->owe_ecdh = crypto_ecdh_init(group); + if (!sta->owe_ecdh) + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + sta->owe_group = group; + + secret = crypto_ecdh_set_peerkey(sta->owe_ecdh, 0, owe_dh + 2, + owe_dh_len - 2); + secret = wpabuf_zeropad(secret, prime_len); + if (!secret) { + wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret); + + /* prk = HKDF-extract(C | A | group, z) */ + + pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); + if (!pub) { + wpabuf_clear_free(secret); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + /* PMKID = Truncate-128(Hash(C | A)) */ + addr[0] = owe_dh + 2; + len[0] = owe_dh_len - 2; + addr[1] = wpabuf_head(pub); + len[1] = wpabuf_len(pub); + if (group == 19) { + res = sha256_vector(2, addr, len, pmkid); + hash_len = SHA256_MAC_LEN; + } else if (group == 20) { + res = sha384_vector(2, addr, len, pmkid); + hash_len = SHA384_MAC_LEN; + } else if (group == 21) { + res = sha512_vector(2, addr, len, pmkid); + hash_len = SHA512_MAC_LEN; + } else { + wpabuf_free(pub); + wpabuf_clear_free(secret); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + pub = wpabuf_zeropad(pub, prime_len); + if (res < 0 || !pub) { + wpabuf_free(pub); + wpabuf_clear_free(secret); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + hkey = wpabuf_alloc(owe_dh_len - 2 + wpabuf_len(pub) + 2); + if (!hkey) { + wpabuf_free(pub); + wpabuf_clear_free(secret); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + wpabuf_put_data(hkey, owe_dh + 2, owe_dh_len - 2); /* C */ + wpabuf_put_buf(hkey, pub); /* A */ + wpabuf_free(pub); + wpabuf_put_le16(hkey, group); /* group */ + if (group == 19) + res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey), + wpabuf_head(secret), wpabuf_len(secret), prk); + else if (group == 20) + res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey), + wpabuf_head(secret), wpabuf_len(secret), prk); + else if (group == 21) + res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey), + wpabuf_head(secret), wpabuf_len(secret), prk); + wpabuf_clear_free(hkey); + wpabuf_clear_free(secret); + if (res < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len); + + /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */ + + os_free(sta->owe_pmk); + sta->owe_pmk = os_malloc(hash_len); + if (!sta->owe_pmk) { + os_memset(prk, 0, SHA512_MAC_LEN); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (group == 19) + res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info, + os_strlen(info), sta->owe_pmk, hash_len); + else if (group == 20) + res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info, + os_strlen(info), sta->owe_pmk, hash_len); + else if (group == 21) + res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info, + os_strlen(info), sta->owe_pmk, hash_len); + os_memset(prk, 0, SHA512_MAC_LEN); + if (res < 0) { + os_free(sta->owe_pmk); + sta->owe_pmk = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + sta->owe_pmk_len = hash_len; + + wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len); + wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN); + wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk, + sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE); + + return WLAN_STATUS_SUCCESS; +} + +#endif /* CONFIG_OWE */ + + static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ies, size_t ies_len, int reassoc) { @@ -1644,32 +2579,20 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } 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; + elems.mdie, elems.mdie_len, + elems.owe_dh, elems.owe_dh_len); + resp = wpa_res_to_status_code(res); if (resp != WLAN_STATUS_SUCCESS) return resp; #ifdef CONFIG_IEEE80211W - if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) == + (WLAN_STA_ASSOC | 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 && + if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) == + (WLAN_STA_ASSOC | WLAN_STA_MFP) && + !sta->sa_query_timed_out && (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) { /* * STA has already been associated with MFP and SA @@ -1690,7 +2613,7 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, sta->flags &= ~WLAN_STA_MFP; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (sta->auth_alg == WLAN_AUTH_FT) { if (!reassoc) { wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried " @@ -1705,9 +2628,13 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, if (resp != WLAN_STATUS_SUCCESS) return resp; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE + if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae && + sta->sae->state == SAE_ACCEPTED) + wpa_auth_add_sae_pmkid(sta->wpa_sm, sta->sae->pmkid); + if (wpa_auth_uses_sae(sta->wpa_sm) && sta->auth_alg == WLAN_AUTH_OPEN) { struct rsn_pmksa_cache_entry *sa; @@ -1731,6 +2658,17 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_SAE */ +#ifdef CONFIG_OWE + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE && + elems.owe_dh) { + resp = owe_process_assoc_req(hapd, sta, elems.owe_dh, + elems.owe_dh_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + } +#endif /* CONFIG_OWE */ + #ifdef CONFIG_IEEE80211N if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) && wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) { @@ -1779,6 +2717,14 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, elems.hs20_len - 4); } else sta->hs20_ie = NULL; + + wpabuf_free(sta->roaming_consortium); + if (elems.roaming_cons_sel) + sta->roaming_consortium = wpabuf_alloc_copy( + elems.roaming_cons_sel + 4, + elems.roaming_cons_sel_len - 4); + else + sta->roaming_consortium = NULL; #endif /* CONFIG_HS20 */ #ifdef CONFIG_FST @@ -1810,6 +2756,14 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled, sizeof(sta->rrm_enabled_capa)); + if (elems.power_capab) { + sta->min_tx_power = elems.power_capab[0]; + sta->max_tx_power = elems.power_capab[1]; + sta->power_capab = 1; + } else { + sta->power_capab = 0; + } + return WLAN_STATUS_SUCCESS; } @@ -1856,7 +2810,8 @@ static int add_associated_sta(struct hostapd_data *hapd, */ if (!sta->added_unassoc && (!(sta->flags & WLAN_STA_AUTHORIZED) || - !wpa_auth_sta_ft_tk_already_set(sta->wpa_sm))) { + (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) && + !wpa_auth_sta_fils_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; @@ -1904,21 +2859,36 @@ static int add_associated_sta(struct hostapd_data *hapd, 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) + const u8 *addr, u16 status_code, int reassoc, + const u8 *ies, size_t ies_len) { int send_len; - u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; + u8 *buf; + size_t buflen; struct ieee80211_mgmt *reply; u8 *p; + u16 res = WLAN_STATUS_SUCCESS; - os_memset(buf, 0, sizeof(buf)); + buflen = sizeof(struct ieee80211_mgmt) + 1024; +#ifdef CONFIG_FILS + if (sta && sta->fils_hlp_resp) + buflen += wpabuf_len(sta->fils_hlp_resp); +#endif /* CONFIG_FILS */ +#ifdef CONFIG_OWE + if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) + buflen += 150; +#endif /* CONFIG_OWE */ + buf = os_zalloc(buflen); + if (!buf) { + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } 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->da, addr, ETH_ALEN); os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN); os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN); @@ -1927,24 +2897,40 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, 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)); + + reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) | + 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) { +#ifdef CONFIG_IEEE80211R_AP + if (sta && 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, + buf + buflen - p, sta->auth_alg, ies, ies_len); + if (!p) { + wpa_printf(MSG_DEBUG, + "FT: Failed to write AssocResp IEs"); + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ + +#ifdef CONFIG_OWE + if (sta && status_code == WLAN_STATUS_SUCCESS && + (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) + p = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, p, + buf + buflen - p, + ies, ies_len); +#endif /* CONFIG_OWE */ #ifdef CONFIG_IEEE80211W - if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) + if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) p = hostapd_eid_assoc_comeback_time(hapd, sta, p); #endif /* CONFIG_IEEE80211W */ @@ -1957,7 +2943,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { u32 nsts = 0, sta_nsts; - if (hapd->conf->use_sta_nsts && sta->vht_capabilities) { + if (sta && hapd->conf->use_sta_nsts && sta->vht_capabilities) { struct ieee80211_vht_capabilities *capa; nsts = (hapd->iface->conf->vht_capab >> @@ -1978,7 +2964,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, p = hostapd_eid_ext_capab(hapd, p); p = hostapd_eid_bss_max_idle_period(hapd, p); - if (sta->qos_map_enabled) + if (sta && sta->qos_map_enabled) p = hostapd_eid_qos_map_set(hapd, p); #ifdef CONFIG_FST @@ -1990,16 +2976,17 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_FST */ #ifdef CONFIG_IEEE80211AC - if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT)) + if (sta && 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) + if (sta && (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)) { + if (sta && + ((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)); @@ -2010,7 +2997,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P - if (sta->p2p_ie && hapd->p2p_group) { + if (sta && sta->p2p_ie && hapd->p2p_group) { struct wpabuf *p2p_resp_ie; enum p2p_status_code status; switch (status_code) { @@ -2039,10 +3026,10 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, p = hostapd_eid_p2p_manage(hapd, p); #endif /* CONFIG_P2P_MANAGER */ - p = hostapd_eid_mbo(hapd, p, buf + sizeof(buf) - p); + p = hostapd_eid_mbo(hapd, p, buf + buflen - p); if (hapd->conf->assocresp_elements && - (size_t) (buf + sizeof(buf) - p) >= + (size_t) (buf + buflen - p) >= wpabuf_len(hapd->conf->assocresp_elements)) { os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements), wpabuf_len(hapd->conf->assocresp_elements)); @@ -2051,14 +3038,174 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, send_len += p - reply->u.assoc_resp.variable; +#ifdef CONFIG_FILS + if (sta && + (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) && + status_code == WLAN_STATUS_SUCCESS) { + struct ieee802_11_elems elems; + + if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) == + ParseFailed || !elems.fils_session) { + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } + + /* FILS Session */ + *p++ = WLAN_EID_EXTENSION; /* Element ID */ + *p++ = 1 + FILS_SESSION_LEN; /* Length */ + *p++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */ + os_memcpy(p, elems.fils_session, FILS_SESSION_LEN); + send_len += 2 + 1 + FILS_SESSION_LEN; + + send_len = fils_encrypt_assoc(sta->wpa_sm, buf, send_len, + buflen, sta->fils_hlp_resp); + if (send_len < 0) { + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } + } +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && + sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) { + struct wpabuf *pub; + + pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); + if (!pub) { + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } + /* OWE Diffie-Hellman Parameter element */ + *p++ = WLAN_EID_EXTENSION; /* Element ID */ + *p++ = 1 + 2 + wpabuf_len(pub); /* Length */ + *p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */ + WPA_PUT_LE16(p, sta->owe_group); + p += 2; + os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub)); + p += wpabuf_len(pub); + send_len += 3 + 2 + wpabuf_len(pub); + wpabuf_free(pub); + } +#endif /* CONFIG_OWE */ + 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; + res = WLAN_STATUS_UNSPECIFIED_FAILURE; } - return WLAN_STATUS_SUCCESS; +done: + os_free(buf); + return res; +} + + +#ifdef CONFIG_OWE +u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *owe_dh, u8 owe_dh_len, + u8 *owe_buf, size_t owe_buf_len, u16 *reason) +{ +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->conf->own_ie_override) { + wpa_printf(MSG_DEBUG, "OWE: Using IE override"); + *reason = WLAN_STATUS_SUCCESS; + return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, + owe_buf_len, NULL, 0); + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) { + wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching"); + owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, + owe_buf_len, NULL, 0); + *reason = WLAN_STATUS_SUCCESS; + return owe_buf; + } + + *reason = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len); + if (*reason != WLAN_STATUS_SUCCESS) + return NULL; + + owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, + owe_buf_len, NULL, 0); + + if (sta->owe_ecdh && owe_buf) { + struct wpabuf *pub; + + pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); + if (!pub) { + *reason = WLAN_STATUS_UNSPECIFIED_FAILURE; + return owe_buf; + } + + /* OWE Diffie-Hellman Parameter element */ + *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */ + *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */ + *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension + */ + WPA_PUT_LE16(owe_buf, sta->owe_group); + owe_buf += 2; + os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub)); + owe_buf += wpabuf_len(pub); + wpabuf_free(pub); + } + + return owe_buf; } +#endif /* CONFIG_OWE */ + + +#ifdef CONFIG_FILS + +void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta) +{ + u16 reply_res; + + wpa_printf(MSG_DEBUG, "FILS: Finish association with " MACSTR, + MAC2STR(sta->addr)); + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); + if (!sta->fils_pending_assoc_req) + return; + reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS, + sta->fils_pending_assoc_is_reassoc, + sta->fils_pending_assoc_req, + sta->fils_pending_assoc_req_len); + os_free(sta->fils_pending_assoc_req); + sta->fils_pending_assoc_req = NULL; + sta->fils_pending_assoc_req_len = 0; + wpabuf_free(sta->fils_hlp_resp); + sta->fils_hlp_resp = NULL; + wpabuf_free(sta->hlp_dhcp_discover); + sta->hlp_dhcp_discover = NULL; + + /* + * Remove the station in case transmission of a success response fails. + * At this point the station was already added associated to the driver. + */ + if (reply_res != WLAN_STATUS_SUCCESS) + hostapd_drv_sta_remove(hapd, sta->addr); +} + + +void fils_hlp_timeout(void *eloop_ctx, void *eloop_data) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = eloop_data; + + wpa_printf(MSG_DEBUG, + "FILS: HLP response timeout - continue with association response for " + MACSTR, MAC2STR(sta->addr)); + if (sta->fils_drv_assoc_finish) + hostapd_notify_assoc_fils_finish(hapd, sta); + else + fils_hlp_finish_assoc(hapd, sta); +} + +#endif /* CONFIG_FILS */ static void handle_assoc(struct hostapd_data *hapd, @@ -2070,6 +3217,13 @@ static void handle_assoc(struct hostapd_data *hapd, const u8 *pos; int left, i; struct sta_info *sta; + u8 *tmp = NULL; + struct hostapd_sta_wpa_psk_short *psk = NULL; + char *identity = NULL; + char *radius_cui = NULL; +#ifdef CONFIG_FILS + int delay_assoc = 0; +#endif /* CONFIG_FILS */ if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : sizeof(mgmt->u.assoc_req))) { @@ -2127,7 +3281,7 @@ static void handle_assoc(struct hostapd_data *hapd, } sta = ap_get_sta(hapd, mgmt->sa); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (sta && sta->auth_alg == WLAN_AUTH_FT && (sta->flags & WLAN_STA_AUTH) == 0) { wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate " @@ -2140,24 +3294,76 @@ static void handle_assoc(struct hostapd_data *hapd, */ sta->flags |= WLAN_STA_AUTH; } else -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ 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 (hapd->iface->current_mode && + hapd->iface->current_mode->mode == + HOSTAPD_MODE_IEEE80211AD) { + int acl_res; + u32 session_timeout, acct_interim_interval; + struct vlan_description vlan_id; + + acl_res = ieee802_11_allowed_address( + hapd, mgmt->sa, (const u8 *) mgmt, len, + &session_timeout, &acct_interim_interval, + &vlan_id, &psk, &identity, &radius_cui, 0); + if (acl_res == HOSTAPD_ACL_REJECT) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Ignore Association Request frame from " + MACSTR " due to ACL reject", + MAC2STR(mgmt->sa)); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + if (acl_res == HOSTAPD_ACL_PENDING) + return; + + /* DMG/IEEE 802.11ad does not use authentication. + * Allocate sta entry upon association. */ + sta = ap_sta_add(hapd, mgmt->sa); + if (!sta) { + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Failed to add STA"); + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + goto fail; + } + + acl_res = ieee802_11_set_radius_info( + hapd, sta, acl_res, session_timeout, + acct_interim_interval, &vlan_id, &psk, + &identity, &radius_cui); + if (acl_res) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Skip authentication for DMG/IEEE 802.11ad"); + sta->flags |= WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->auth_alg = WLAN_AUTH_OPEN; + } else { + 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) { + 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", @@ -2169,7 +3375,7 @@ static void handle_assoc(struct hostapd_data *hapd, WLAN_FC_STYPE_ASSOC_REQ; if (hapd->tkip_countermeasures) { - resp = WLAN_REASON_MICHAEL_MIC_FAILURE; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } @@ -2195,6 +3401,32 @@ static void handle_assoc(struct hostapd_data *hapd, */ sta->capability = capab_info; +#ifdef CONFIG_FILS + if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) { + int res; + + /* The end of the payload is encrypted. Need to decrypt it + * before parsing. */ + + tmp = os_memdup(pos, left); + if (!tmp) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + res = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt, + len, tmp, left); + if (res < 0) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + pos = tmp; + left = res; + } +#endif /* CONFIG_FILS */ + /* followed by SSID and Supported rates; and HT capabilities if 802.11n * is used */ resp = check_assoc_ies(hapd, sta, pos, left, reassoc); @@ -2210,7 +3442,8 @@ static void handle_assoc(struct hostapd_data *hapd, sta->listen_interval = listen_interval; - if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) + if (hapd->iface->current_mode && + 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) { @@ -2229,7 +3462,8 @@ static void handle_assoc(struct hostapd_data *hapd, !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 == + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_slot_time == 1) ieee802_11_set_beacons(hapd->iface); @@ -2244,7 +3478,8 @@ static void handle_assoc(struct hostapd_data *hapd, !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 + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_preamble == 1) ieee802_11_set_beacons(hapd->iface); } @@ -2281,7 +3516,22 @@ static void handle_assoc(struct hostapd_data *hapd, taxonomy_sta_info_assoc_req(hapd, sta, pos, left); #endif /* CONFIG_TAXONOMY */ + sta->pending_wds_enable = 0; + +#ifdef CONFIG_FILS + if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) { + if (fils_process_hlp(hapd, sta, pos, left) > 0) + delay_assoc = 1; + } +#endif /* CONFIG_FILS */ + fail: + os_free(identity); + os_free(radius_cui); + hostapd_free_psk_list(psk); + /* * In case of a successful response, add the station to the driver. * Otherwise, the kernel may ignore Data frames before we process the @@ -2300,18 +3550,44 @@ static void handle_assoc(struct hostapd_data *hapd, * issues with processing other non-Data Class 3 frames during this * window. */ - if (resp == WLAN_STATUS_SUCCESS && add_associated_sta(hapd, sta)) + if (resp == WLAN_STATUS_SUCCESS && sta && 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); +#ifdef CONFIG_FILS + if (sta) { + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); + os_free(sta->fils_pending_assoc_req); + sta->fils_pending_assoc_req = NULL; + sta->fils_pending_assoc_req_len = 0; + wpabuf_free(sta->fils_hlp_resp); + sta->fils_hlp_resp = NULL; + } + if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS) { + sta->fils_pending_assoc_req = tmp; + sta->fils_pending_assoc_req_len = left; + sta->fils_pending_assoc_is_reassoc = reassoc; + sta->fils_drv_assoc_finish = 0; + wpa_printf(MSG_DEBUG, + "FILS: Waiting for HLP processing before sending (Re)Association Response frame to " + MACSTR, MAC2STR(sta->addr)); + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); + eloop_register_timeout(0, hapd->conf->fils_hlp_wait_time * 1024, + fils_hlp_timeout, hapd, sta); + return; + } +#endif /* CONFIG_FILS */ + + reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc, pos, + left); + os_free(tmp); /* * 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) { + if (sta && ((reply_res != WLAN_STATUS_SUCCESS && + resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc)) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } @@ -2368,6 +3644,17 @@ static void handle_disassoc(struct hostapd_data *hapd, mlme_disassociate_indication( hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code)); + + /* DMG/IEEE 802.11ad does not use deauthication. Deallocate sta upon + * disassociation. */ + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { + sta->flags &= ~WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "deauthenticated"); + ap_free_sta(hapd, sta); + } } @@ -2462,12 +3749,13 @@ static int robust_action_frame(u8 category) static int handle_action(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, size_t len) + const struct ieee80211_mgmt *mgmt, size_t len, + unsigned int freq) { struct sta_info *sta; - sta = ap_get_sta(hapd, mgmt->sa); + u8 *action __maybe_unused; - if (len < IEEE80211_HDRLEN + 1) { + if (len < IEEE80211_HDRLEN + 2 + 1) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "handle_action - too short payload (len=%lu)", @@ -2475,11 +3763,19 @@ static int handle_action(struct hostapd_data *hapd, return 0; } + action = (u8 *) &mgmt->u.action.u; + wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR + " da " MACSTR " len %d freq %u", + mgmt->u.action.category, *action, + MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) len, freq); + + sta = ap_get_sta(hapd, mgmt->sa); + 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); + mgmt->u.action.category, MAC2STR(mgmt->sa)); return 0; } @@ -2516,14 +3812,14 @@ static int handle_action(struct hostapd_data *hapd, } switch (mgmt->u.action.category) { -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP 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 */ +#endif /* CONFIG_IEEE80211R_AP */ case WLAN_ACTION_WMM: hostapd_wmm_action(hapd, mgmt, len); return 1; @@ -2531,11 +3827,11 @@ static int handle_action(struct hostapd_data *hapd, case WLAN_ACTION_SA_QUERY: return hostapd_sa_query_action(hapd, mgmt, len); #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP case WLAN_ACTION_WNM: ieee802_11_rx_wnm_action_ap(hapd, mgmt, len); return 1; -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ #ifdef CONFIG_FST case WLAN_ACTION_FST: if (hapd->iface->fst) @@ -2551,12 +3847,41 @@ static int handle_action(struct hostapd_data *hapd, 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); + return 1; } #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_DPP + if (len >= IEEE80211_HDRLEN + 6 && + mgmt->u.action.u.vs_public_action.action == + WLAN_PA_VENDOR_SPECIFIC && + WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) == + OUI_WFA && + mgmt->u.action.u.vs_public_action.variable[0] == + DPP_OUI_TYPE) { + const u8 *pos, *end; + + pos = mgmt->u.action.u.vs_public_action.oui; + end = ((const u8 *) mgmt) + len; + hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos, + freq); + return 1; + } + if (len >= IEEE80211_HDRLEN + 2 && + (mgmt->u.action.u.public_action.action == + WLAN_PA_GAS_INITIAL_RESP || + mgmt->u.action.u.public_action.action == + WLAN_PA_GAS_COMEBACK_RESP)) { + const u8 *pos, *end; + + pos = &mgmt->u.action.u.public_action.action; + end = ((const u8 *) mgmt) + len; + gas_query_ap_rx(hapd->gas, mgmt->sa, + mgmt->u.action.category, + pos, end - pos, hapd->iface->freq); + return 1; + } +#endif /* CONFIG_DPP */ if (hapd->public_action_cb) { hapd->public_action_cb(hapd->public_action_cb_ctx, (u8 *) mgmt, len, @@ -2600,10 +3925,9 @@ static int handle_action(struct hostapd_data *hapd, */ wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action " "frame back to sender"); - resp = os_malloc(len); + resp = os_memdup(mgmt, 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); @@ -2639,10 +3963,17 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, struct ieee80211_mgmt *mgmt; u16 fc, stype; int ret = 0; + unsigned int freq; + int ssi_signal = fi ? fi->ssi_signal : 0; if (len < 24) return 0; + if (fi && fi->freq) + freq = fi->freq; + else + freq = hapd->iface->freq; + mgmt = (struct ieee80211_mgmt *) buf; fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); @@ -2669,11 +4000,13 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, if (stype == WLAN_FC_STYPE_PROBE_REQ) { - handle_probe_req(hapd, mgmt, len, fi->ssi_signal); + handle_probe_req(hapd, mgmt, len, ssi_signal); return 1; } - if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { + if ((!is_broadcast_ether_addr(mgmt->da) || + stype != WLAN_FC_STYPE_ACTION) && + 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", @@ -2682,7 +4015,7 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, } if (hapd->iconf->track_sta_max_num) - sta_track_add(hapd->iface, mgmt->sa); + sta_track_add(hapd->iface, mgmt->sa, ssi_signal); switch (stype) { case WLAN_FC_STYPE_AUTH: @@ -2712,7 +4045,7 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, break; case WLAN_FC_STYPE_ACTION: wpa_printf(MSG_DEBUG, "mgmt::action"); - ret = handle_action(hapd, mgmt, len); + ret = handle_action(hapd, mgmt, len, freq); break; default: hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, @@ -2734,7 +4067,8 @@ static void handle_auth_cb(struct hostapd_data *hapd, sta = ap_get_sta(hapd, mgmt->da); if (!sta) { - wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found", + wpa_printf(MSG_DEBUG, "handle_auth_cb: STA " MACSTR + " not found", MAC2STR(mgmt->da)); return; } @@ -2856,11 +4190,15 @@ static void handle_assoc_cb(struct hostapd_data *hapd, 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) || + if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && + !hapd->conf->osen) || + sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK || sta->auth_alg == WLAN_AUTH_FT) { /* - * Open, static WEP, or FT protocol; no separate authorization - * step. + * Open, static WEP, FT protocol, or FILS; no separate + * authorization step. */ ap_sta_set_authorized(hapd, sta, 1); } @@ -2874,16 +4212,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd, 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, @@ -2900,6 +4228,27 @@ static void handle_assoc_cb(struct hostapd_data *hapd, hostapd_set_sta_flags(hapd, sta); + if (!(sta->flags & WLAN_STA_WDS) && sta->pending_wds_enable) { + wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for STA " + MACSTR " based on pending request", + MAC2STR(sta->addr)); + sta->pending_wds_enable = 0; + sta->flags |= WLAN_STA_WDS; + } + + if (sta->flags & WLAN_STA_WDS) { + int ret; + char ifname_wds[IFNAMSIZ + 1]; + + wpa_printf(MSG_DEBUG, "Reenable 4-address WDS mode for STA " + MACSTR " (aid %u)", + MAC2STR(sta->addr), sta->aid); + 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->auth_alg == WLAN_AUTH_FT) wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); else @@ -2907,6 +4256,18 @@ static void handle_assoc_cb(struct hostapd_data *hapd, hapd->new_assoc_sta_cb(hapd, sta, !new_assoc); ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); +#ifdef CONFIG_FILS + if ((sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) && + fils_set_tk(sta->wpa_sm) < 0) { + wpa_printf(MSG_DEBUG, "FILS: TK configuration failed"); + ap_sta_disconnect(hapd, sta, sta->addr, + WLAN_REASON_UNSPECIFIED); + return; + } +#endif /* CONFIG_FILS */ + if (sta->pending_eapol_rx) { struct os_reltime now, age; @@ -2976,6 +4337,65 @@ static void handle_disassoc_cb(struct hostapd_data *hapd, } +static void handle_action_cb(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok) +{ + struct sta_info *sta; + const struct rrm_measurement_report_element *report; + + if (is_multicast_ether_addr(mgmt->da)) + return; +#ifdef CONFIG_DPP + if (len >= IEEE80211_HDRLEN + 6 && + mgmt->u.action.category == WLAN_ACTION_PUBLIC && + mgmt->u.action.u.vs_public_action.action == + WLAN_PA_VENDOR_SPECIFIC && + WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) == + OUI_WFA && + mgmt->u.action.u.vs_public_action.variable[0] == + DPP_OUI_TYPE) { + const u8 *pos, *end; + + pos = &mgmt->u.action.u.vs_public_action.variable[1]; + end = ((const u8 *) mgmt) + len; + hostapd_dpp_tx_status(hapd, mgmt->da, pos, end - pos, ok); + return; + } + if (len >= IEEE80211_HDRLEN + 2 && + mgmt->u.action.category == WLAN_ACTION_PUBLIC && + (mgmt->u.action.u.public_action.action == + WLAN_PA_GAS_INITIAL_REQ || + mgmt->u.action.u.public_action.action == + WLAN_PA_GAS_COMEBACK_REQ)) { + const u8 *pos, *end; + + pos = mgmt->u.action.u.public_action.variable; + end = ((const u8 *) mgmt) + len; + gas_query_ap_tx_status(hapd->gas, mgmt->da, pos, end - pos, ok); + return; + } +#endif /* CONFIG_DPP */ + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + wpa_printf(MSG_DEBUG, "handle_action_cb: STA " MACSTR + " not found", MAC2STR(mgmt->da)); + return; + } + + if (len < 24 + 5 + sizeof(*report)) + return; + report = (const struct rrm_measurement_report_element *) + &mgmt->u.action.u.rrm.variable[2]; + if (mgmt->u.action.category == WLAN_ACTION_RADIO_MEASUREMENT && + mgmt->u.action.u.rrm.action == WLAN_RRM_RADIO_MEASUREMENT_REQUEST && + report->eid == WLAN_EID_MEASURE_REQUEST && + report->len >= 3 && + report->type == MEASURE_TYPE_BEACON) + hostapd_rrm_beacon_req_tx_status(hapd, mgmt, len, ok); +} + + /** * ieee802_11_mgmt_cb - Process management frame TX status callback * @hapd: hostapd BSS data structure (the BSS from which the management frame @@ -2993,8 +4413,16 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, #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); + size_t hex_len = 2 * len + 1; + char *hex = os_malloc(hex_len); + + if (hex) { + wpa_snprintf_hex(hex, hex_len, buf, len); + wpa_msg(hapd->msg_ctx, MSG_INFO, + "MGMT-TX-STATUS stype=%u ok=%d buf=%s", + stype, ok, hex); + os_free(hex); + } return; } #endif /* CONFIG_TESTING_OPTIONS */ @@ -3025,6 +4453,7 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, break; case WLAN_FC_STYPE_ACTION: wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok); + handle_action_cb(hapd, mgmt, len, ok); break; default: wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype); @@ -3139,10 +4568,22 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, struct sta_info *sta; sta = ap_get_sta(hapd, src); - if (sta && (sta->flags & WLAN_STA_ASSOC)) { + if (sta && + ((sta->flags & WLAN_STA_ASSOC) || + ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) { if (!hapd->conf->wds_sta) return; + if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK)) == + WLAN_STA_ASSOC_REQ_OK) { + wpa_printf(MSG_DEBUG, + "Postpone 4-address WDS mode enabling for STA " + MACSTR " since TX status for AssocResp is not yet known", + MAC2STR(sta->addr)); + sta->pending_wds_enable = 1; + return; + } + if (wds && !(sta->flags & WLAN_STA_WDS)) { int ret; char ifname_wds[IFNAMSIZ + 1]; diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 0327dec2a2bc..2f3b4da8e752 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -16,6 +16,8 @@ struct hostapd_frame_info; struct ieee80211_ht_capabilities; struct ieee80211_vht_capabilities; struct ieee80211_mgmt; +struct vlan_description; +struct hostapd_sta_wpa_psk_short; int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, struct hostapd_frame_info *fi); @@ -55,6 +57,8 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid); int hostapd_ht_operation_update(struct hostapd_iface *iface); void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, @@ -135,4 +139,31 @@ void ap_copy_sta_supp_op_classes(struct sta_info *sta, const u8 *supp_op_classes, size_t supp_op_classes_len); +u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid); +void ieee802_11_finish_fils_auth(struct hostapd_data *hapd, + struct sta_info *sta, int success, + struct wpabuf *erp_resp, + const u8 *msk, size_t msk_len); +u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *owe_dh, u8 owe_dh_len, + u8 *owe_buf, size_t owe_buf_len, u16 *reason); +void fils_hlp_timeout(void *eloop_ctx, void *eloop_data); +void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta); +void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *pos, size_t len, u16 auth_alg, + u16 auth_transaction, u16 status_code, + void (*cb)(struct hostapd_data *hapd, + struct sta_info *sta, + u16 resp, struct wpabuf *data, int pub)); + +size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd); +u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, size_t len); +int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr, + const u8 *msg, size_t len, u32 *session_timeout, + u32 *acct_interim_interval, + struct vlan_description *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui, + int is_probe_req); + #endif /* IEEE802_11_H */ diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c index b8905373618d..5cb7fb1454f7 100644 --- a/src/ap/ieee802_11_auth.c +++ b/src/ap/ieee802_11_auth.c @@ -244,6 +244,7 @@ int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr, * @psk: Linked list buffer for returning WPA PSK * @identity: Buffer for returning identity (from RADIUS) * @radius_cui: Buffer for returning CUI (from RADIUS) + * @is_probe_req: Whether this query for a Probe Request frame * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING * * The caller is responsible for freeing the returned *identity and *radius_cui @@ -254,7 +255,8 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, u32 *acct_interim_interval, struct vlan_description *vlan_id, struct hostapd_sta_wpa_psk_short **psk, - char **identity, char **radius_cui) + char **identity, char **radius_cui, + int is_probe_req) { int res; @@ -281,6 +283,12 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, #else /* CONFIG_NO_RADIUS */ struct hostapd_acl_query_data *query; + if (is_probe_req) { + /* Skip RADIUS queries for Probe Request frames to avoid + * excessive load on the authentication server. */ + return HOSTAPD_ACL_ACCEPT; + }; + /* Check whether ACL cache has an entry for this station */ res = hostapd_acl_cache_get(hapd, addr, session_timeout, acct_interim_interval, vlan_id, psk, @@ -327,14 +335,13 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, return HOSTAPD_ACL_REJECT; } - query->auth_msg = os_malloc(len); + query->auth_msg = os_memdup(msg, len); if (query->auth_msg == NULL) { wpa_printf(MSG_ERROR, "Failed to allocate memory for " "auth frame."); hostapd_acl_query_free(query); return HOSTAPD_ACL_REJECT; } - os_memcpy(query->auth_msg, msg, len); query->auth_msg_len = len; query->next = hapd->acl_queries; hapd->acl_queries = query; @@ -665,9 +672,11 @@ void hostapd_acl_deinit(struct hostapd_data *hapd) #ifndef CONFIG_NO_RADIUS hostapd_acl_cache_free(hapd->acl_cache); + hapd->acl_cache = NULL; #endif /* CONFIG_NO_RADIUS */ query = hapd->acl_queries; + hapd->acl_queries = NULL; while (query) { prev = query; query = query->next; diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h index 71f53b9612fa..5aece5183c69 100644 --- a/src/ap/ieee802_11_auth.h +++ b/src/ap/ieee802_11_auth.h @@ -23,7 +23,8 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, u32 *acct_interim_interval, struct vlan_description *vlan_id, struct hostapd_sta_wpa_psk_short **psk, - char **identity, char **radius_cui); + char **identity, char **radius_cui, + int is_probe_req); int hostapd_acl_init(struct hostapd_data *hapd); void hostapd_acl_deinit(struct hostapd_data *hapd); void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk); diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c new file mode 100644 index 000000000000..1a8d46972985 --- /dev/null +++ b/src/ap/ieee802_11_he.c @@ -0,0 +1,88 @@ +/* + * hostapd / IEEE 802.11ax HE + * Copyright (c) 2016-2017, Qualcomm Atheros, Inc. + * + * 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 "common/ieee802_11_defs.h" +#include "hostapd.h" +#include "ap_config.h" +#include "beacon.h" +#include "ieee802_11.h" +#include "dfs.h" + +u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_he_capabilities *cap; + u8 *pos = eid; + + if (!hapd->iface->current_mode) + return eid; + + *pos++ = WLAN_EID_EXTENSION; + *pos++ = 1 + sizeof(struct ieee80211_he_capabilities); + *pos++ = WLAN_EID_EXT_HE_CAPABILITIES; + + cap = (struct ieee80211_he_capabilities *) pos; + os_memset(cap, 0, sizeof(*cap)); + + if (hapd->iface->conf->he_phy_capab.he_su_beamformer) + cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |= + HE_PHYCAP_SU_BEAMFORMER_CAPAB; + + if (hapd->iface->conf->he_phy_capab.he_su_beamformee) + cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |= + HE_PHYCAP_SU_BEAMFORMEE_CAPAB; + + if (hapd->iface->conf->he_phy_capab.he_mu_beamformer) + cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |= + HE_PHYCAP_MU_BEAMFORMER_CAPAB; + + pos += sizeof(*cap); + + return pos; +} + + +u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_he_operation *oper; + u8 *pos = eid; + + if (!hapd->iface->current_mode) + return eid; + + *pos++ = WLAN_EID_EXTENSION; + *pos++ = 1 + sizeof(struct ieee80211_he_operation); + *pos++ = WLAN_EID_EXT_HE_OPERATION; + + oper = (struct ieee80211_he_operation *) pos; + os_memset(oper, 0, sizeof(*oper)); + + if (hapd->iface->conf->he_op.he_bss_color) + oper->he_oper_params |= hapd->iface->conf->he_op.he_bss_color; + + if (hapd->iface->conf->he_op.he_default_pe_duration) + oper->he_oper_params |= + (hapd->iface->conf->he_op.he_default_pe_duration << + HE_OPERATION_DFLT_PE_DURATION_OFFSET); + + if (hapd->iface->conf->he_op.he_twt_required) + oper->he_oper_params |= HE_OPERATION_TWT_REQUIRED; + + if (hapd->iface->conf->he_op.he_rts_threshold) + oper->he_oper_params |= + (hapd->iface->conf->he_op.he_rts_threshold << + HE_OPERATION_RTS_THRESHOLD_OFFSET); + + /* TODO: conditional MaxBSSID Indicator subfield */ + + pos += sizeof(*oper); + + return pos; +} diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c index 5eb1060a2965..214855dccb8c 100644 --- a/src/ap/ieee802_11_ht.c +++ b/src/ap/ieee802_11_ht.c @@ -236,17 +236,29 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, int i; const u8 *start = (const u8 *) mgmt; const u8 *data = start + IEEE80211_HDRLEN + 2; + struct sta_info *sta; + + wpa_printf(MSG_DEBUG, + "HT: Received 20/40 BSS Coexistence Management frame from " + MACSTR, MAC2STR(mgmt->sa)); hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d", mgmt->u.action.u.public_action.action); - if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { + wpa_printf(MSG_DEBUG, + "Ignore 20/40 BSS Coexistence Management frame since 40 MHz capability is not enabled"); return; + } - if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) + if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) { + wpa_printf(MSG_DEBUG, + "Ignore too short 20/40 BSS Coexistence Management frame"); return; + } + /* 20/40 BSS Coexistence element */ bc_ie = (struct ieee80211_2040_bss_coex_ie *) data; if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE || bc_ie->length < 1) { @@ -254,13 +266,35 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, bc_ie->element_id, bc_ie->length); return; } - if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) + if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) { + wpa_printf(MSG_DEBUG, + "Truncated 20/40 BSS Coexistence element"); return; + } data += 2 + bc_ie->length; - wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x", - bc_ie->coex_param); + wpa_printf(MSG_DEBUG, + "20/40 BSS Coexistence Information field: 0x%x (%s%s%s%s%s%s)", + bc_ie->coex_param, + (bc_ie->coex_param & BIT(0)) ? "[InfoReq]" : "", + (bc_ie->coex_param & BIT(1)) ? "[40MHzIntolerant]" : "", + (bc_ie->coex_param & BIT(2)) ? "[20MHzBSSWidthReq]" : "", + (bc_ie->coex_param & BIT(3)) ? "[OBSSScanExemptionReq]" : "", + (bc_ie->coex_param & BIT(4)) ? + "[OBSSScanExemptionGrant]" : "", + (bc_ie->coex_param & (BIT(5) | BIT(6) | BIT(7))) ? + "[Reserved]" : ""); + if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) { + /* Intra-BSS communication prohibiting 20/40 MHz BSS operation + */ + sta = ap_get_sta(hapd, mgmt->sa); + if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, + "Ignore intra-BSS 20/40 BSS Coexistence Management frame from not-associated STA"); + return; + } + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -269,6 +303,8 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, } if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) { + /* Inter-BSS communication prohibiting 20/40 MHz BSS operation + */ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -276,12 +312,16 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, is_ht40_allowed = 0; } - if (start + len - data >= 3 && - data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) { + /* 20/40 BSS Intolerant Channel Report element (zero or more times) */ + while (start + len - data >= 3 && + data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) { u8 ielen = data[1]; - if (ielen > start + len - data - 2) + if (ielen > start + len - data - 2) { + wpa_printf(MSG_DEBUG, + "Truncated 20/40 BSS Intolerant Channel Report element"); return; + } ic_report = (struct ieee80211_2040_intol_chan_report *) data; wpa_printf(MSG_DEBUG, "20/40 BSS Intolerant Channel Report: Operating Class %u", @@ -292,8 +332,10 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, for (i = 0; i < ielen - 1; i++) { u8 chan = ic_report->variable[i]; + if (chan == iface->conf->channel) + continue; /* matching own primary channel */ if (is_40_allowed(iface, chan)) - continue; + continue; /* not within affected channels */ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -301,6 +343,8 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, chan); is_ht40_allowed = 0; } + + data += 2 + ielen; } wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d", is_ht40_allowed, iface->num_sta_ht40_intolerant); @@ -340,8 +384,8 @@ u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, * that did not specify a valid WMM IE in the (Re)Association Request * frame. */ - if (!ht_capab || - !(sta->flags & WLAN_STA_WMM) || hapd->conf->disable_11n) { + if (!ht_capab || !(sta->flags & WLAN_STA_WMM) || + !hapd->iconf->ieee80211n || hapd->conf->disable_11n) { sta->flags &= ~WLAN_STA_HT; os_free(sta->ht_capabilities); sta->ht_capabilities = NULL; diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index 259413bd12ff..49e9bf8cc7c9 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -178,6 +178,10 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) case 1: /* Bits 8-15 */ if (hapd->conf->proxy_arp) *pos |= 0x10; /* Bit 12 - Proxy ARP */ + if (hapd->conf->coloc_intf_reporting) { + /* Bit 13 - Collocated Interference Reporting */ + *pos |= 0x20; + } break; case 2: /* Bits 16-23 */ if (hapd->conf->wnm_sleep_mode) @@ -186,9 +190,9 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) *pos |= 0x08; /* Bit 19 - BSS Transition */ break; case 3: /* Bits 24-31 */ -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP *pos |= 0x02; /* Bit 25 - SSID List */ -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ if (hapd->conf->time_advertisement == 2) *pos |= 0x08; /* Bit 27 - UTC TSF Offset */ if (hapd->conf->interworking) @@ -218,12 +222,21 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) if (hapd->conf->ssid.utf8_ssid) *pos |= 0x01; /* Bit 48 - UTF-8 SSID */ break; + case 7: /* Bits 56-63 */ + break; case 8: /* Bits 64-71 */ if (hapd->conf->ftm_responder) *pos |= 0x40; /* Bit 70 - FTM responder */ if (hapd->conf->ftm_initiator) *pos |= 0x80; /* Bit 71 - FTM initiator */ break; + case 9: /* Bits 72-79 */ +#ifdef CONFIG_FILS + if ((hapd->conf->wpa & WPA_PROTO_RSN) && + wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) + *pos |= 0x01; +#endif /* CONFIG_FILS */ + break; } } @@ -246,10 +259,10 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) if (len < 9 && (hapd->conf->ftm_initiator || hapd->conf->ftm_responder)) len = 9; -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP if (len < 4) len = 4; -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ #ifdef CONFIG_HS20 if (hapd->conf->hs20 && len < 6) len = 6; @@ -258,6 +271,11 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) if (hapd->conf->mbo_enabled && len < 6) len = 6; #endif /* CONFIG_MBO */ +#ifdef CONFIG_FILS + if ((!(hapd->conf->wpa & WPA_PROTO_RSN) || + !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) && len < 10) + len = 10; +#endif /* CONFIG_FILS */ if (len < hapd->iface->extended_capa_len) len = hapd->iface->extended_capa_len; if (len == 0) @@ -432,7 +450,7 @@ u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid) { size_t len; - if (hapd->conf->time_advertisement != 2) + if (hapd->conf->time_advertisement != 2 || !hapd->conf->time_zone) return eid; len = os_strlen(hapd->conf->time_zone); @@ -503,7 +521,7 @@ u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP if (hapd->conf->ap_max_inactivity > 0) { unsigned int val; *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD; @@ -521,7 +539,7 @@ u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) pos += 2; *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */ } -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ return pos; } @@ -531,23 +549,38 @@ u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len) { - u8 mbo[6], *mbo_pos = mbo; + u8 mbo[9], *mbo_pos = mbo; u8 *pos = eid; - if (!hapd->conf->mbo_enabled) + if (!hapd->conf->mbo_enabled && + !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) return eid; - *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND; - *mbo_pos++ = 1; - /* Not Cellular aware */ - *mbo_pos++ = 0; + if (hapd->conf->mbo_enabled) { + *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND; + *mbo_pos++ = 1; + /* Not Cellular aware */ + *mbo_pos++ = 0; + } - if (hapd->mbo_assoc_disallow) { + if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) { *mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW; *mbo_pos++ = 1; *mbo_pos++ = hapd->mbo_assoc_disallow; } + if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) { + u8 ctrl; + + ctrl = OCE_RELEASE; + if (OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) + ctrl |= OCE_IS_STA_CFON; + + *mbo_pos++ = OCE_ATTR_ID_CAPA_IND; + *mbo_pos++ = 1; + *mbo_pos++ = ctrl; + } + pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo); return pos; @@ -556,19 +589,91 @@ u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len) u8 hostapd_mbo_ie_len(struct hostapd_data *hapd) { - if (!hapd->conf->mbo_enabled) + u8 len; + + if (!hapd->conf->mbo_enabled && + !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) return 0; /* * MBO IE header (6) + Capability Indication attribute (3) + * Association Disallowed attribute (3) = 12 */ - return 6 + 3 + (hapd->mbo_assoc_disallow ? 3 : 0); + len = 6; + if (hapd->conf->mbo_enabled) + len += 3 + (hapd->mbo_assoc_disallow ? 3 : 0); + + /* OCE capability indication attribute (3) */ + if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) + len += 3; + + return len; } #endif /* CONFIG_MBO */ +#ifdef CONFIG_OWE +static int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd) +{ + return hapd->conf->owe_transition_ssid_len > 0 && + !is_zero_ether_addr(hapd->conf->owe_transition_bssid); +} +#endif /* CONFIG_OWE */ + + +size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd) +{ +#ifdef CONFIG_OWE + if (!hostapd_eid_owe_trans_enabled(hapd)) + return 0; + return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len; +#else /* CONFIG_OWE */ + return 0; +#endif /* CONFIG_OWE */ +} + + +u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, + size_t len) +{ +#ifdef CONFIG_OWE + u8 *pos = eid; + size_t elen; + + if (hapd->conf->owe_transition_ifname[0] && + !hostapd_eid_owe_trans_enabled(hapd)) + hostapd_owe_trans_get_info(hapd); + + if (!hostapd_eid_owe_trans_enabled(hapd)) + return pos; + + elen = hostapd_eid_owe_trans_len(hapd); + if (len < elen) { + wpa_printf(MSG_DEBUG, + "OWE: Not enough room in the buffer for OWE IE"); + return pos; + } + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = elen - 2; + WPA_PUT_BE24(pos, OUI_WFA); + pos += 3; + *pos++ = OWE_OUI_TYPE; + os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN); + pos += ETH_ALEN; + *pos++ = hapd->conf->owe_transition_ssid_len; + os_memcpy(pos, hapd->conf->owe_transition_ssid, + hapd->conf->owe_transition_ssid_len); + pos += hapd->conf->owe_transition_ssid_len; + + return pos; +#else /* CONFIG_OWE */ + return eid; +#endif /* CONFIG_OWE */ +} + + void ap_copy_sta_supp_op_classes(struct sta_info *sta, const u8 *supp_op_classes, size_t supp_op_classes_len) @@ -584,3 +689,66 @@ void ap_copy_sta_supp_op_classes(struct sta_info *sta, os_memcpy(sta->supp_op_classes + 1, supp_op_classes, supp_op_classes_len); } + + +u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid) +{ + u8 *pos = eid; +#ifdef CONFIG_FILS + u8 *len; + u16 fils_info = 0; + size_t realms; + struct fils_realm *realm; + + if (!(hapd->conf->wpa & WPA_PROTO_RSN) || + !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) + return pos; + + realms = dl_list_len(&hapd->conf->fils_realms); + if (realms > 7) + realms = 7; /* 3 bit count field limits this to max 7 */ + + *pos++ = WLAN_EID_FILS_INDICATION; + len = pos++; + /* TODO: B0..B2: Number of Public Key Identifiers */ + if (hapd->conf->erp_domain) { + /* B3..B5: Number of Realm Identifiers */ + fils_info |= realms << 3; + } + /* TODO: B6: FILS IP Address Configuration */ + if (hapd->conf->fils_cache_id_set) + fils_info |= BIT(7); + if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) + fils_info |= BIT(8); /* HESSID Included */ + /* FILS Shared Key Authentication without PFS Supported */ + fils_info |= BIT(9); + if (hapd->conf->fils_dh_group) { + /* FILS Shared Key Authentication with PFS Supported */ + fils_info |= BIT(10); + } + /* TODO: B11: FILS Public Key Authentication Supported */ + /* B12..B15: Reserved */ + WPA_PUT_LE16(pos, fils_info); + pos += 2; + if (hapd->conf->fils_cache_id_set) { + os_memcpy(pos, hapd->conf->fils_cache_id, FILS_CACHE_ID_LEN); + pos += FILS_CACHE_ID_LEN; + } + if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) { + os_memcpy(pos, hapd->conf->hessid, ETH_ALEN); + pos += ETH_ALEN; + } + + dl_list_for_each(realm, &hapd->conf->fils_realms, struct fils_realm, + list) { + if (realms == 0) + break; + realms--; + os_memcpy(pos, realm->hash, 2); + pos += 2; + } + *len = pos - len - 1; +#endif /* CONFIG_FILS */ + + return pos; +} diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c index f30f63bc5709..8d0662078bcf 100644 --- a/src/ap/ieee802_11_vht.c +++ b/src/ap/ieee802_11_vht.c @@ -334,7 +334,7 @@ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, { /* Disable VHT caps for STAs associated to no-VHT BSSes. */ if (!vht_capab || - hapd->conf->disable_11ac || + !hapd->iconf->ieee80211ac || hapd->conf->disable_11ac || !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) { sta->flags &= ~WLAN_STA_VHT; os_free(sta->vht_capabilities); diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index 80ff996948f9..185279f9e3ef 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -31,6 +31,8 @@ #include "ap_drv_ops.h" #include "wps_hostapd.h" #include "hs20.h" +/* FIX: Not really a good thing to require ieee802_11.h here.. (FILS) */ +#include "ieee802_11.h" #include "ieee802_1x.h" @@ -316,6 +318,7 @@ static void ieee802_1x_learn_identity(struct hostapd_data *hapd, hdr->code != EAP_CODE_INITIATE)) return; + eap_erp_update_identity(sm->eap, eap, len); identity = eap_get_identity(sm->eap, &identity_len); if (identity == NULL) return; @@ -472,7 +475,7 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd, } } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && sta->wpa_sm && (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) || @@ -485,7 +488,7 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd, wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id"); return -1; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm && add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0) @@ -588,9 +591,9 @@ int add_common_radius_attr(struct hostapd_data *hapd, } -static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, - struct sta_info *sta, - const u8 *eap, size_t len) +void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *eap, size_t len) { struct radius_msg *msg; struct eapol_state_machine *sm = sta->eapol_sm; @@ -680,6 +683,8 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, #ifdef CONFIG_HS20 if (hapd->conf->hs20) { u8 ver = 1; /* Release 2 */ + if (HS20_VERSION > 0x10) + ver = 2; /* Release 3 */ if (!radius_msg_add_wfa( msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION, &ver, 1)) { @@ -709,6 +714,41 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, goto fail; } } + + if (sta->roaming_consortium && + !radius_msg_add_wfa( + msg, RADIUS_VENDOR_ATTR_WFA_HS20_ROAMING_CONSORTIUM, + wpabuf_head(sta->roaming_consortium), + wpabuf_len(sta->roaming_consortium))) { + wpa_printf(MSG_ERROR, + "Could not add HS 2.0 Roaming Consortium"); + goto fail; + } + + if (hapd->conf->t_c_filename) { + be32 timestamp; + + if (!radius_msg_add_wfa( + msg, + RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILENAME, + (const u8 *) hapd->conf->t_c_filename, + os_strlen(hapd->conf->t_c_filename))) { + wpa_printf(MSG_ERROR, + "Could not add HS 2.0 T&C Filename"); + goto fail; + } + + timestamp = host_to_be32(hapd->conf->t_c_timestamp); + if (!radius_msg_add_wfa( + msg, + RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP, + (const u8 *) ×tamp, + sizeof(timestamp))) { + wpa_printf(MSG_ERROR, + "Could not add HS 2.0 Timestamp"); + goto fail; + } + } } #endif /* CONFIG_HS20 */ @@ -845,7 +885,7 @@ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, } -static struct eapol_state_machine * +struct eapol_state_machine * ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta) { int flags = 0; @@ -970,7 +1010,9 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, } key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); - if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) { + if (key_mgmt != -1 && + (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE || + key_mgmt == WPA_KEY_MGMT_DPP)) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - " "STA is using PSK"); return; @@ -1113,7 +1155,9 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) } key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); - if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) { + if (key_mgmt != -1 && + (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE || + key_mgmt == WPA_KEY_MGMT_DPP)) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK"); /* * Clear any possible EAPOL authenticator state to support @@ -1154,7 +1198,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) sta->eapol_sm->eap_if->portEnabled = TRUE; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (sta->auth_alg == WLAN_AUTH_FT) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, @@ -1170,10 +1214,32 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) sta->eapol_sm->portValid = TRUE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); - /* TODO: get vlan_id from R0KH using RRB message */ + ap_sta_bind_vlan(hapd, sta); + return; + } +#endif /* CONFIG_IEEE80211R_AP */ + +#ifdef CONFIG_FILS + if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, + "PMK from FILS - skip IEEE 802.1X/EAP"); + /* Setup EAPOL state machines to already authenticated state + * because of existing FILS information. */ + sta->eapol_sm->keyRun = TRUE; + sta->eapol_sm->eap_if->eapKeyAvailable = TRUE; + sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING; + sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; + sta->eapol_sm->authSuccess = TRUE; + sta->eapol_sm->authFail = FALSE; + sta->eapol_sm->portValid = TRUE; + if (sta->eapol_sm->eap) + eap_sm_notify_cached(sta->eapol_sm->eap); return; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_FILS */ pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); if (pmksa) { @@ -1395,11 +1461,10 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, } } while (class_len < 1); - nclass[nclass_count].data = os_malloc(class_len); + nclass[nclass_count].data = os_memdup(attr_class, class_len); if (nclass[nclass_count].data == NULL) break; - os_memcpy(nclass[nclass_count].data, attr_class, class_len); nclass[nclass_count].len = class_len; nclass_count++; } @@ -1559,6 +1624,33 @@ static void ieee802_1x_hs20_session_info(struct hostapd_data *hapd, ap_sta_session_warning_timeout(hapd, sta, warning_time); } + +static void ieee802_1x_hs20_t_c_filtering(struct hostapd_data *hapd, + struct sta_info *sta, u8 *pos, + size_t len) +{ + if (len < 4) + return; /* Malformed information */ + wpa_printf(MSG_DEBUG, + "HS 2.0: Terms and Conditions filtering %02x %02x %02x %02x", + pos[0], pos[1], pos[2], pos[3]); + hs20_t_c_filtering(hapd, sta, pos[0] & BIT(0)); +} + + +static void ieee802_1x_hs20_t_c_url(struct hostapd_data *hapd, + struct sta_info *sta, u8 *pos, size_t len) +{ + os_free(sta->t_c_url); + sta->t_c_url = os_malloc(len + 1); + if (!sta->t_c_url) + return; + os_memcpy(sta->t_c_url, pos, len); + sta->t_c_url[len] = '\0'; + wpa_printf(MSG_DEBUG, + "HS 2.0: Terms and Conditions URL %s", sta->t_c_url); +} + #endif /* CONFIG_HS20 */ @@ -1606,6 +1698,12 @@ static void ieee802_1x_check_hs20(struct hostapd_data *hapd, ieee802_1x_hs20_session_info(hapd, sta, pos, sublen, session_timeout); break; + case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING: + ieee802_1x_hs20_t_c_filtering(hapd, sta, pos, sublen); + break; + case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL: + ieee802_1x_hs20_t_c_url(hapd, sta, pos, sublen); + break; } } #endif /* CONFIG_HS20 */ @@ -1663,6 +1761,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, struct sta_info *sta; u32 session_timeout = 0, termination_action, acct_interim_interval; int session_timeout_set; + u32 reason_code; struct eapol_state_machine *sm; int override_eapReq = 0; struct radius_hdr *hdr = radius_msg_get_hdr(msg); @@ -1788,14 +1887,17 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, break; sta->session_timeout_set = !!session_timeout_set; - sta->session_timeout = session_timeout; + os_get_reltime(&sta->session_timeout); + sta->session_timeout.sec += session_timeout; /* RFC 3580, Ch. 3.17 */ if (session_timeout_set && termination_action == - RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) { + RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) sm->reAuthPeriod = session_timeout; - } else if (session_timeout_set) + else if (session_timeout_set) ap_sta_session_timeout(hapd, sta, session_timeout); + else + ap_sta_no_session_timeout(hapd, sta); sm->eap_if->aaaSuccess = TRUE; override_eapReq = 1; @@ -1811,6 +1913,13 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, case RADIUS_CODE_ACCESS_REJECT: sm->eap_if->aaaFail = TRUE; override_eapReq = 1; + if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE, + &reason_code) == 0) { + wpa_printf(MSG_DEBUG, + "RADIUS server indicated WLAN-Reason-Code %u in Access-Reject for " + MACSTR, reason_code, MAC2STR(sta->addr)); + sta->disconnect_reason_code = reason_code; + } break; case RADIUS_CODE_ACCESS_CHALLENGE: sm->eap_if->aaaEapReq = TRUE; @@ -1837,6 +1946,19 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, if (override_eapReq) sm->eap_if->aaaEapReq = FALSE; +#ifdef CONFIG_FILS +#ifdef NEED_AP_MLME + if (sta->flags & WLAN_STA_PENDING_FILS_ERP) { + /* TODO: Add a PMKSA entry on success? */ + ieee802_11_finish_fils_auth( + hapd, sta, hdr->code == RADIUS_CODE_ACCESS_ACCEPT, + sm->eap_if->aaaEapReqData, + sm->eap_if->aaaEapKeyData, + sm->eap_if->aaaEapKeyDataLen); + } +#endif /* NEED_AP_MLME */ +#endif /* CONFIG_FILS */ + eapol_auth_step(sm); return RADIUS_RX_QUEUED; @@ -1924,7 +2046,7 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d", eapol->default_wep_key_idx); - + if (ieee802_1x_rekey_broadcast(hapd)) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "failed to generate a " @@ -2034,13 +2156,19 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, } if (eap_user->password) { - user->password = os_malloc(eap_user->password_len); + user->password = os_memdup(eap_user->password, + eap_user->password_len); if (user->password == NULL) goto out; - os_memcpy(user->password, eap_user->password, - eap_user->password_len); user->password_len = eap_user->password_len; user->password_hash = eap_user->password_hash; + if (eap_user->salt && eap_user->salt_len) { + user->salt = os_memdup(eap_user->salt, + eap_user->salt_len); + if (!user->salt) + goto out; + user->salt_len = eap_user->salt_len; + } } user->force_version = eap_user->force_version; user->macacl = eap_user->macacl; @@ -2190,6 +2318,7 @@ int ieee802_1x_init(struct hostapd_data *hapd) conf.erp_domain = hapd->conf->erp_domain; conf.erp = hapd->conf->eap_server_erp; conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; + conf.tls_flags = hapd->conf->tls_flags; conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key; conf.eap_fast_a_id = hapd->conf->eap_fast_a_id; conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len; @@ -2326,6 +2455,16 @@ int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta, MAC2STR(sta->addr), xhdr->version, xhdr->type, be_to_host16(xhdr->length), ack); +#ifdef CONFIG_WPS + if (xhdr->type == IEEE802_1X_TYPE_EAP_PACKET && ack && + (sta->flags & WLAN_STA_WPS) && + ap_sta_pending_delayed_1x_auth_fail_disconnect(hapd, sta)) { + wpa_printf(MSG_DEBUG, + "WPS: Indicate EAP completion on ACK for EAP-Failure"); + hostapd_wps_eap_completed(hapd); + } +#endif /* CONFIG_WPS */ + if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY) return 0; @@ -2642,6 +2781,15 @@ static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx) hs20_send_wnm_notification_deauth_req(hapd, sta->addr, sta->hs20_deauth_req); } + + if (sta->hs20_t_c_filtering) { + wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " + MACSTR " to indicate Terms and Conditions filtering", + MAC2STR(sta->addr)); + hs20_send_wnm_notification_t_c(hapd, sta->addr, sta->t_c_url); + os_free(sta->t_c_url); + sta->t_c_url = NULL; + } } #endif /* CONFIG_HS20 */ @@ -2655,6 +2803,7 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, /* TODO: get PMKLifetime from WPA parameters */ static const int dot11RSNAConfigPMKLifetime = 43200; unsigned int session_timeout; + struct os_reltime now, remaining; #ifdef CONFIG_HS20 if (remediation && !sta->remediation) { @@ -2665,7 +2814,8 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, sta->remediation_method = 1; /* SOAP-XML SPP */ } - if (success && (sta->remediation || sta->hs20_deauth_req)) { + if (success && (sta->remediation || sta->hs20_deauth_req || + sta->hs20_t_c_filtering)) { wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to " MACSTR " in 100 ms", MAC2STR(sta->addr)); eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta); @@ -2675,10 +2825,13 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, #endif /* CONFIG_HS20 */ key = ieee802_1x_get_key(sta->eapol_sm, &len); - if (sta->session_timeout_set) - session_timeout = sta->session_timeout; - else + if (sta->session_timeout_set) { + os_get_reltime(&now); + os_reltime_sub(&sta->session_timeout, &now, &remaining); + session_timeout = (remaining.sec > 0) ? remaining.sec : 1; + } else { session_timeout = dot11RSNAConfigPMKLifetime; + } if (success && key && len >= PMK_LEN && !sta->remediation && !sta->hs20_deauth_requested && wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout, @@ -2699,15 +2852,6 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, * EAP-FAST with anonymous provisioning, may require another * EAPOL authentication to be started to complete connection. */ - wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "IEEE 802.1X: Force " - "disconnection after EAP-Failure"); - /* Add a small sleep to increase likelihood of previously - * requested EAP-Failure TX getting out before this should the - * driver reorder operations. - */ - os_sleep(0, 10000); - ap_sta_disconnect(hapd, sta, sta->addr, - WLAN_REASON_IEEE_802_1X_AUTH_FAILED); - hostapd_wps_eap_completed(hapd); + ap_sta_delayed_1x_auth_fail_disconnect(hapd, sta); } } diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h index ec80199007b6..9594661be8ee 100644 --- a/src/ap/ieee802_1x.h +++ b/src/ap/ieee802_1x.h @@ -57,5 +57,10 @@ int add_common_radius_attr(struct hostapd_data *hapd, struct hostapd_radius_attr *req_attr, struct sta_info *sta, struct radius_msg *msg); +void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *eap, size_t len); +struct eapol_state_machine * +ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta); #endif /* IEEE802_1X_H */ diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c index 3c086bfc7131..4d6a92e086bd 100644 --- a/src/ap/ndisc_snoop.c +++ b/src/ap/ndisc_snoop.c @@ -182,4 +182,5 @@ int ndisc_snoop_init(struct hostapd_data *hapd) void ndisc_snoop_deinit(struct hostapd_data *hapd) { l2_packet_deinit(hapd->sock_ndisc); + hapd->sock_ndisc = NULL; } diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c index a2efff618286..b8fd5924b889 100644 --- a/src/ap/neighbor_db.c +++ b/src/ap/neighbor_db.c @@ -43,6 +43,7 @@ static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr) nr->civic = NULL; os_memset(nr->bssid, 0, sizeof(nr->bssid)); os_memset(&nr->ssid, 0, sizeof(nr->ssid)); + nr->stationary = 0; } @@ -64,7 +65,7 @@ hostapd_neighbor_add(struct hostapd_data *hapd) int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid, const struct wpa_ssid_value *ssid, const struct wpabuf *nr, const struct wpabuf *lci, - const struct wpabuf *civic) + const struct wpabuf *civic, int stationary) { struct hostapd_neighbor_entry *entry; @@ -83,18 +84,20 @@ int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid, if (!entry->nr) goto fail; - if (lci) { + if (lci && wpabuf_len(lci)) { entry->lci = wpabuf_dup(lci); if (!entry->lci || os_get_time(&entry->lci_date)) goto fail; } - if (civic) { + if (civic && wpabuf_len(civic)) { entry->civic = wpabuf_dup(civic); if (!entry->civic) goto fail; } + entry->stationary = stationary; + return 0; fail: diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h index c22e043c120e..ba46d88435e5 100644 --- a/src/ap/neighbor_db.h +++ b/src/ap/neighbor_db.h @@ -16,7 +16,7 @@ hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid, int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid, const struct wpa_ssid_value *ssid, const struct wpabuf *nr, const struct wpabuf *lci, - const struct wpabuf *civic); + const struct wpabuf *civic, int stationary); int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid, const struct wpa_ssid_value *ssid); void hostpad_free_neighbor_db(struct hostapd_data *hapd); diff --git a/src/ap/peerkey_auth.c b/src/ap/peerkey_auth.c deleted file mode 100644 index efc1d7e4c78f..000000000000 --- a/src/ap/peerkey_auth.c +++ /dev/null @@ -1,396 +0,0 @@ -/* - * hostapd - PeerKey for Direct Link Setup (DLS) - * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> - * - * 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 "crypto/sha1.h" -#include "crypto/sha256.h" -#include "crypto/random.h" -#include "wpa_auth.h" -#include "wpa_auth_i.h" -#include "wpa_auth_ie.h" - -#ifdef CONFIG_PEERKEY - -static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx) -{ -#if 0 - struct wpa_authenticator *wpa_auth = eloop_ctx; - struct wpa_stsl_negotiation *neg = timeout_ctx; -#endif - - /* TODO: ? */ -} - - -struct wpa_stsl_search { - const u8 *addr; - struct wpa_state_machine *sm; -}; - - -static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx) -{ - struct wpa_stsl_search *search = ctx; - if (os_memcmp(search->addr, sm->addr, ETH_ALEN) == 0) { - search->sm = sm; - return 1; - } - return 0; -} - - -static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, const u8 *peer, - u16 mui, u16 error_type) -{ - u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN + - 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)]; - u8 *pos; - struct rsn_error_kde error; - - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, - "Sending SMK Error"); - - pos = kde; - - if (peer) { - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, - NULL, 0); - } - - error.mui = host_to_be16(mui); - error.error_type = host_to_be16(error_type); - pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR, - (u8 *) &error, sizeof(error), NULL, 0); - - __wpa_send_eapol(wpa_auth, sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR, - NULL, NULL, kde, pos - kde, 0, 0, 0); -} - - -void wpa_smk_m1(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key, - const u8 *key_data, size_t key_data_len) -{ - struct wpa_eapol_ie_parse kde; - struct wpa_stsl_search search; - u8 *buf, *pos; - size_t buf_len; - - if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1"); - return; - } - - if (kde.rsn_ie == NULL || kde.mac_addr == NULL || - kde.mac_addr_len < ETH_ALEN) { - wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " - "SMK M1"); - return; - } - - /* Initiator = sm->addr; Peer = kde.mac_addr */ - - search.addr = kde.mac_addr; - search.sm = NULL; - if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == - 0 || search.sm == NULL) { - wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR - " aborted - STA not associated anymore", - MAC2STR(kde.mac_addr)); - wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, - STK_ERR_STA_NR); - /* FIX: wpa_stsl_remove(wpa_auth, neg); */ - return; - } - - buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; - buf = os_malloc(buf_len); - if (buf == NULL) - return; - /* Initiator RSN IE */ - os_memcpy(buf, kde.rsn_ie, kde.rsn_ie_len); - pos = buf + kde.rsn_ie_len; - /* Initiator MAC Address */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN, - NULL, 0); - - /* SMK M2: - * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, - * MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE) - */ - - wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG, - "Sending SMK M2"); - - __wpa_send_eapol(wpa_auth, search.sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE, - NULL, key->key_nonce, buf, pos - buf, 0, 0, 0); - - os_free(buf); -} - - -static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - struct wpa_eapol_key *key, - struct wpa_eapol_ie_parse *kde, - const u8 *smk) -{ - u8 *buf, *pos; - size_t buf_len; - u32 lifetime; - - /* SMK M4: - * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce, - * MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE, - * Lifetime KDE) - */ - - buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN + - 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + - 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + - 2 + RSN_SELECTOR_LEN + sizeof(lifetime); - pos = buf = os_malloc(buf_len); - if (buf == NULL) - return; - - /* Initiator MAC Address */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN, - NULL, 0); - - /* Initiator Nonce */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN, - NULL, 0); - - /* SMK with PNonce */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, - key->key_nonce, WPA_NONCE_LEN); - - /* Lifetime */ - lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, - (u8 *) &lifetime, sizeof(lifetime), NULL, 0); - - wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "Sending SMK M4"); - - __wpa_send_eapol(wpa_auth, sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE, - NULL, key->key_nonce, buf, pos - buf, 0, 1, 0); - - os_free(buf); -} - - -static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - struct wpa_eapol_key *key, - struct wpa_eapol_ie_parse *kde, - const u8 *smk, const u8 *peer) -{ - u8 *buf, *pos; - size_t buf_len; - u32 lifetime; - - /* SMK M5: - * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, - * MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE, - * Lifetime KDE)) - */ - - buf_len = kde->rsn_ie_len + - 2 + RSN_SELECTOR_LEN + ETH_ALEN + - 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + - 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + - 2 + RSN_SELECTOR_LEN + sizeof(lifetime); - pos = buf = os_malloc(buf_len); - if (buf == NULL) - return; - - /* Peer RSN IE */ - os_memcpy(pos, kde->rsn_ie, kde->rsn_ie_len); - pos += kde->rsn_ie_len; - - /* Peer MAC Address */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0); - - /* PNonce */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce, - WPA_NONCE_LEN, NULL, 0); - - /* SMK and INonce */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, - kde->nonce, WPA_NONCE_LEN); - - /* Lifetime */ - lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, - (u8 *) &lifetime, sizeof(lifetime), NULL, 0); - - wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "Sending SMK M5"); - - __wpa_send_eapol(wpa_auth, sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_SMK_MESSAGE, - NULL, kde->nonce, buf, pos - buf, 0, 1, 0); - - os_free(buf); -} - - -void wpa_smk_m3(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key, - const u8 *key_data, size_t key_data_len) -{ - struct wpa_eapol_ie_parse kde; - struct wpa_stsl_search search; - u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos; - - if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3"); - return; - } - - if (kde.rsn_ie == NULL || - kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || - kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) { - wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or " - "Nonce KDE in SMK M3"); - return; - } - - /* Peer = sm->addr; Initiator = kde.mac_addr; - * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */ - - search.addr = kde.mac_addr; - search.sm = NULL; - if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == - 0 || search.sm == NULL) { - wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR - " aborted - STA not associated anymore", - MAC2STR(kde.mac_addr)); - wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, - STK_ERR_STA_NR); - /* FIX: wpa_stsl_remove(wpa_auth, neg); */ - return; - } - - if (random_get_bytes(smk, PMK_LEN)) { - wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK"); - return; - } - - /* SMK = PRF-256(Random number, "SMK Derivation", - * AA || Time || INonce || PNonce) - */ - os_memcpy(buf, wpa_auth->addr, ETH_ALEN); - pos = buf + ETH_ALEN; - wpa_get_ntp_timestamp(pos); - pos += 8; - os_memcpy(pos, kde.nonce, WPA_NONCE_LEN); - pos += WPA_NONCE_LEN; - os_memcpy(pos, key->key_nonce, WPA_NONCE_LEN); -#ifdef CONFIG_IEEE80211W - sha256_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), - smk, PMK_LEN); -#else /* CONFIG_IEEE80211W */ - sha1_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), - smk, PMK_LEN); -#endif /* CONFIG_IEEE80211W */ - - wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, PMK_LEN); - - wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk); - wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr); - - /* Authenticator does not need SMK anymore and it is required to forget - * it. */ - os_memset(smk, 0, sizeof(*smk)); -} - - -void wpa_smk_error(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - const u8 *key_data, size_t key_data_len) -{ - struct wpa_eapol_ie_parse kde; - struct wpa_stsl_search search; - struct rsn_error_kde error; - u16 mui, error_type; - - if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); - return; - } - - if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || - kde.error == NULL || kde.error_len < sizeof(error)) { - wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in " - "SMK Error"); - return; - } - - search.addr = kde.mac_addr; - search.sm = NULL; - if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == - 0 || search.sm == NULL) { - wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not " - "associated for SMK Error message from " MACSTR, - MAC2STR(kde.mac_addr), MAC2STR(sm->addr)); - return; - } - - os_memcpy(&error, kde.error, sizeof(error)); - mui = be_to_host16(error.mui); - error_type = be_to_host16(error.error_type); - wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, - "STA reported SMK Error: Peer " MACSTR - " MUI %d Error Type %d", - MAC2STR(kde.mac_addr), mui, error_type); - - wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type); -} - - -int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, - struct wpa_stsl_negotiation *neg) -{ - struct wpa_stsl_negotiation *pos, *prev; - - if (wpa_auth == NULL) - return -1; - pos = wpa_auth->stsl_negotiations; - prev = NULL; - while (pos) { - if (pos == neg) { - if (prev) - prev->next = pos->next; - else - wpa_auth->stsl_negotiations = pos->next; - - eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos); - os_free(pos); - return 0; - } - prev = pos; - pos = pos->next; - } - - return -1; -} - -#endif /* CONFIG_PEERKEY */ diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c index d610e7e5b005..15e2c4943f2b 100644 --- a/src/ap/pmksa_cache_auth.c +++ b/src/ap/pmksa_cache_auth.c @@ -282,7 +282,42 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa, int session_timeout, struct eapol_state_machine *eapol, int akmp) { - struct rsn_pmksa_cache_entry *entry, *pos; + struct rsn_pmksa_cache_entry *entry; + + entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, kck, kck_len, + aa, spa, session_timeout, eapol, + akmp); + + if (pmksa_cache_auth_add_entry(pmksa, entry) < 0) + return NULL; + + return entry; +} + + +/** + * pmksa_cache_auth_create_entry - Create a PMKSA cache entry + * @pmk: The new pairwise master key + * @pmk_len: PMK length in bytes, usually PMK_LEN (32) + * @pmkid: Calculated PMKID + * @kck: Key confirmation key or %NULL if not yet derived + * @kck_len: KCK length in bytes + * @aa: Authenticator address + * @spa: Supplicant address + * @session_timeout: Session timeout + * @eapol: Pointer to EAPOL state machine data + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: Pointer to the added PMKSA cache entry or %NULL on error + * + * This function creates a PMKSA entry. + */ +struct rsn_pmksa_cache_entry * +pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid, + const u8 *kck, size_t kck_len, const u8 *aa, + const u8 *spa, int session_timeout, + struct eapol_state_machine *eapol, int akmp) +{ + struct rsn_pmksa_cache_entry *entry; struct os_reltime now; if (pmk_len > PMK_LEN_MAX) @@ -303,8 +338,7 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, else if (wpa_key_mgmt_suite_b(akmp)) rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); else - rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, - wpa_key_mgmt_sha256(akmp)); + rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp); os_get_reltime(&now); entry->expiration = now.sec; if (session_timeout > 0) @@ -315,9 +349,30 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, os_memcpy(entry->spa, spa, ETH_ALEN); pmksa_cache_from_eapol_data(entry, eapol); + return entry; +} + + +/** + * pmksa_cache_auth_add_entry - Add a PMKSA cache entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + * @entry: Pointer to PMKSA cache entry + * + * This function adds PMKSA cache entry to the PMKSA cache. If an old entry is + * already in the cache for the same Supplicant, this entry will be replaced + * with the new entry. PMKID will be calculated based on the PMK. + */ +int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ + struct rsn_pmksa_cache_entry *pos; + + if (entry == NULL) + return -1; + /* Replace an old entry for the same STA (if found) with the new entry */ - pos = pmksa_cache_auth_get(pmksa, spa, NULL); + pos = pmksa_cache_auth_get(pmksa, entry->spa, NULL); if (pos) pmksa_cache_free_entry(pmksa, pos); @@ -331,7 +386,7 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, pmksa_cache_link_entry(pmksa, entry); - return entry; + return 0; } @@ -462,7 +517,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0) continue; rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid, - wpa_key_mgmt_sha256(entry->akmp)); + entry->akmp); if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0) return entry; } @@ -605,3 +660,70 @@ int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) } return pos - buf; } + + +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL +#ifdef CONFIG_MESH + +/** + * pmksa_cache_auth_list_mesh - Dump text list of entries in PMKSA cache + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + * @addr: MAC address of the peer (NULL means any) + * @buf: Buffer for the list + * @len: Length of the buffer + * Returns: Number of bytes written to buffer + * + * This function is used to generate a text format representation of the + * current PMKSA cache contents for the ctrl_iface PMKSA_GET command to store + * in external storage. + */ +int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr, + char *buf, size_t len) +{ + int ret; + char *pos, *end; + struct rsn_pmksa_cache_entry *entry; + struct os_reltime now; + + pos = buf; + end = buf + len; + os_get_reltime(&now); + + + /* + * Entry format: + * <BSSID> <PMKID> <PMK> <expiration in seconds> + */ + for (entry = pmksa->pmksa; entry; entry = entry->next) { + if (addr && os_memcmp(entry->spa, addr, ETH_ALEN) != 0) + continue; + + ret = os_snprintf(pos, end - pos, MACSTR " ", + MAC2STR(entry->spa)); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + + pos += wpa_snprintf_hex(pos, end - pos, entry->pmkid, + PMKID_LEN); + + ret = os_snprintf(pos, end - pos, " "); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + + pos += wpa_snprintf_hex(pos, end - pos, entry->pmk, + entry->pmk_len); + + ret = os_snprintf(pos, end - pos, " %d\n", + (int) (entry->expiration - now.sec)); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + } + + return pos - buf; +} + +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h index d8d9c5a25c0e..2ef217435b11 100644 --- a/src/ap/pmksa_cache_auth.h +++ b/src/ap/pmksa_cache_auth.h @@ -35,6 +35,7 @@ struct rsn_pmksa_cache_entry { }; struct rsn_pmksa_cache; +struct radius_das_attrs; struct rsn_pmksa_cache * pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, @@ -53,6 +54,13 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa, int session_timeout, struct eapol_state_machine *eapol, int akmp); struct rsn_pmksa_cache_entry * +pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid, + const u8 *kck, size_t kck_len, const u8 *aa, + const u8 *spa, int session_timeout, + struct eapol_state_machine *eapol, int akmp); +int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry); +struct rsn_pmksa_cache_entry * pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, const struct rsn_pmksa_cache_entry *old_entry, const u8 *aa, const u8 *pmkid); @@ -65,5 +73,7 @@ int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa, struct radius_das_attrs *attr); int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa); +int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr, + char *buf, size_t len); #endif /* PMKSA_CACHE_H */ diff --git a/src/ap/rrm.c b/src/ap/rrm.c index 3569f955bcd2..56ed29c1c068 100644 --- a/src/ap/rrm.c +++ b/src/ap/rrm.c @@ -2,6 +2,7 @@ * hostapd / Radio Measurement (RRM) * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * Copyright (c) 2016-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +11,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "common/wpa_ctrl.h" #include "hostapd.h" #include "ap_drv_ops.h" #include "sta_info.h" @@ -69,24 +71,47 @@ static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token, } +static void hostapd_handle_beacon_report(struct hostapd_data *hapd, + const u8 *addr, u8 token, u8 rep_mode, + const u8 *pos, size_t len) +{ + char report[2 * 255 + 1]; + + wpa_printf(MSG_DEBUG, "Beacon report token %u len %zu from " MACSTR, + token, len, MAC2STR(addr)); + /* Skip to the beginning of the Beacon report */ + if (len < 3) + return; + pos += 3; + len -= 3; + report[0] = '\0'; + if (wpa_snprintf_hex(report, sizeof(report), pos, len) < 0) + return; + wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s", + MAC2STR(addr), token, rep_mode, report); +} + + static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd, const u8 *buf, size_t len) { const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; const u8 *pos, *ie, *end; - u8 token; + u8 token, rep_mode; end = buf + len; token = mgmt->u.action.u.rrm.dialog_token; pos = mgmt->u.action.u.rrm.variable; while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) { - if (ie[1] < 5) { + if (ie[1] < 3) { wpa_printf(MSG_DEBUG, "Bad Measurement Report element"); break; } - wpa_printf(MSG_DEBUG, "Measurement report type %u", ie[4]); + rep_mode = ie[3]; + wpa_printf(MSG_DEBUG, "Measurement report mode 0x%x type %u", + rep_mode, ie[4]); switch (ie[4]) { case MEASURE_TYPE_LCI: @@ -95,6 +120,10 @@ static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd, case MEASURE_TYPE_FTM_RANGE: hostapd_handle_range_report(hapd, token, ie + 2, ie[1]); break; + case MEASURE_TYPE_BEACON: + hostapd_handle_beacon_report(hapd, mgmt->sa, token, + rep_mode, ie + 2, ie[1]); + break; default: wpa_printf(MSG_DEBUG, "Measurement report type %u is not supported", @@ -118,7 +147,7 @@ static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len) /* Subelements are arranged as IEs */ subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE); if (subelem && subelem[1] == 2) - return *(u16 *) (subelem + 2); + return WPA_GET_LE16(subelem + 2); return 0; } @@ -129,12 +158,12 @@ static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age) struct os_time curr, diff; unsigned long diff_l; + if (nr->stationary || max_age == 0xffff) + return 1; + if (!max_age) return 0; - if (max_age == 0xffff) - return 1; - if (os_get_time(&curr)) return 0; @@ -341,13 +370,7 @@ int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr) struct sta_info *sta = ap_get_sta(hapd, addr); int ret; - if (!sta) { - wpa_printf(MSG_INFO, - "Request LCI: Destination address is not in station list"); - return -1; - } - - if (!(sta->flags & WLAN_STA_AUTHORIZED)) { + if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) { wpa_printf(MSG_INFO, "Request LCI: Destination address is not connected"); return -1; @@ -450,9 +473,8 @@ int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr, wpa_printf(MSG_DEBUG, "Request range: Range request is already in process; overriding"); hapd->range_req_active = 0; - eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0, - hostapd_range_rep_timeout_handler, hapd, - NULL); + eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, + NULL); } /* Action + measurement type + token + reps + EID + len = 7 */ @@ -542,3 +564,111 @@ void hostapd_clean_rrm(struct hostapd_data *hapd) eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL); hapd->range_req_active = 0; } + + +int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr, + u8 req_mode, const struct wpabuf *req) +{ + struct wpabuf *buf; + struct sta_info *sta = ap_get_sta(hapd, addr); + int ret; + enum beacon_report_mode mode; + const u8 *pos; + + /* Request data: + * Operating Class (1), Channel Number (1), Randomization Interval (2), + * Measurement Duration (2), Measurement Mode (1), BSSID (6), + * Optional Subelements (variable) + */ + if (wpabuf_len(req) < 13) { + wpa_printf(MSG_INFO, "Beacon request: Too short request data"); + return -1; + } + pos = wpabuf_head(req); + mode = pos[6]; + + if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) { + wpa_printf(MSG_INFO, + "Beacon request: " MACSTR " is not connected", + MAC2STR(addr)); + return -1; + } + + switch (mode) { + case BEACON_REPORT_MODE_PASSIVE: + if (!(sta->rrm_enabled_capa[0] & + WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE)) { + wpa_printf(MSG_INFO, + "Beacon request: " MACSTR + " does not support passive beacon report", + MAC2STR(addr)); + return -1; + } + break; + case BEACON_REPORT_MODE_ACTIVE: + if (!(sta->rrm_enabled_capa[0] & + WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE)) { + wpa_printf(MSG_INFO, + "Beacon request: " MACSTR + " does not support active beacon report", + MAC2STR(addr)); + return -1; + } + break; + case BEACON_REPORT_MODE_TABLE: + if (!(sta->rrm_enabled_capa[0] & + WLAN_RRM_CAPS_BEACON_REPORT_TABLE)) { + wpa_printf(MSG_INFO, + "Beacon request: " MACSTR + " does not support table beacon report", + MAC2STR(addr)); + return -1; + } + break; + default: + wpa_printf(MSG_INFO, + "Beacon request: Unknown measurement mode %d", mode); + return -1; + } + + buf = wpabuf_alloc(5 + 2 + 3 + wpabuf_len(req)); + if (!buf) + return -1; + + hapd->beacon_req_token++; + if (!hapd->beacon_req_token) + hapd->beacon_req_token++; + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST); + wpabuf_put_u8(buf, hapd->beacon_req_token); + wpabuf_put_le16(buf, 0); /* Number of repetitions */ + + /* Measurement Request element */ + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, 3 + wpabuf_len(req)); + wpabuf_put_u8(buf, 1); /* Measurement Token */ + wpabuf_put_u8(buf, req_mode); /* Measurement Request Mode */ + wpabuf_put_u8(buf, MEASURE_TYPE_BEACON); /* Measurement Type */ + wpabuf_put_buf(buf, req); + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); + if (ret < 0) + return ret; + + return hapd->beacon_req_token; +} + + +void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok) +{ + if (len < 24 + 3) + return; + wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_REQ_TX_STATUS MACSTR + " %u ack=%d", MAC2STR(mgmt->da), + mgmt->u.action.u.rrm.dialog_token, ok); +} diff --git a/src/ap/rrm.h b/src/ap/rrm.h index f07fd41ac019..02cd522ee9d6 100644 --- a/src/ap/rrm.h +++ b/src/ap/rrm.h @@ -24,5 +24,10 @@ int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr, u16 random_interval, u8 min_ap, const u8 *responders, unsigned int n_responders); void hostapd_clean_rrm(struct hostapd_data *hapd); +int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr, + u8 req_mode, const struct wpabuf *req); +void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok); #endif /* RRM_H */ diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index f12d4088b131..179cf43b60b1 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -1,6 +1,6 @@ /* * hostapd / Station table - * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -17,6 +17,7 @@ #include "radius/radius_client.h" #include "p2p/p2p.h" #include "fst/fst.h" +#include "crypto/crypto.h" #include "hostapd.h" #include "accounting.h" #include "ieee802_1x.h" @@ -36,6 +37,7 @@ #include "ndisc_snoop.h" #include "sta_info.h" #include "vlan.h" +#include "wps_hostapd.h" static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, struct sta_info *sta); @@ -47,6 +49,7 @@ static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx); static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx); #endif /* CONFIG_IEEE80211W */ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta); +static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx); int ap_for_each_sta(struct hostapd_data *hapd, int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, @@ -194,7 +197,8 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) if (sta->no_short_slot_time_set) { sta->no_short_slot_time_set = 0; hapd->iface->num_sta_no_short_slot_time--; - if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_slot_time == 0) set_beacon++; } @@ -202,7 +206,8 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) if (sta->no_short_preamble_set) { sta->no_short_preamble_set = 0; hapd->iface->num_sta_no_short_preamble--; - if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_preamble == 0) set_beacon++; } @@ -316,6 +321,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) wpabuf_free(sta->wps_ie); wpabuf_free(sta->p2p_ie); wpabuf_free(sta->hs20_ie); + wpabuf_free(sta->roaming_consortium); #ifdef CONFIG_FST wpabuf_free(sta->mb_ies); #endif /* CONFIG_FST */ @@ -326,6 +332,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) os_free(sta->identity); os_free(sta->radius_cui); os_free(sta->remediation_url); + os_free(sta->t_c_url); wpabuf_free(sta->hs20_deauth_req); os_free(sta->hs20_session_info_url); @@ -337,6 +344,31 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) mbo_ap_sta_free(sta); os_free(sta->supp_op_classes); +#ifdef CONFIG_FILS + os_free(sta->fils_pending_assoc_req); + wpabuf_free(sta->fils_hlp_resp); + wpabuf_free(sta->hlp_dhcp_discover); + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); +#ifdef CONFIG_FILS_SK_PFS + crypto_ecdh_deinit(sta->fils_ecdh); + wpabuf_clear_free(sta->fils_dh_ss); + wpabuf_free(sta->fils_g_sta); +#endif /* CONFIG_FILS_SK_PFS */ +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + bin_clear_free(sta->owe_pmk, sta->owe_pmk_len); + crypto_ecdh_deinit(sta->owe_ecdh); +#endif /* CONFIG_OWE */ + + os_free(sta->ext_capability); + +#ifdef CONFIG_WNM_AP + eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta); +#endif /* CONFIG_WNM_AP */ + + os_free(sta->ifname_wds); + os_free(sta); } @@ -597,7 +629,7 @@ void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta) static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx) { -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; @@ -608,7 +640,7 @@ static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx) wnm_send_ess_disassoc_imminent(hapd, sta, sta->hs20_session_info_url, sta->hs20_disassoc_timer); -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ } @@ -745,9 +777,17 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; - sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { + /* Skip deauthentication in DMG/IEEE 802.11ad */ + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | + WLAN_STA_ASSOC_REQ_OK); + sta->timeout_next = STA_REMOVE; + } else { + sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); + sta->timeout_next = STA_DEAUTH; + } ap_sta_set_authorized(hapd, sta, 0); - sta->timeout_next = STA_DEAUTH; wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " "for " MACSTR " (%d seconds - " "AP_MAX_INACTIVITY_AFTER_DISASSOC)", @@ -783,6 +823,14 @@ static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx) void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason) { + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { + /* Deauthentication is not used in DMG/IEEE 802.11ad; + * disassociate the STA instead. */ + ap_sta_disassociate(hapd, sta, reason); + return; + } + wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; @@ -1229,6 +1277,20 @@ void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, ap_handle_timer, hapd, sta); sta->timeout_next = STA_REMOVE; + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { + /* Deauthentication is not used in DMG/IEEE 802.11ad; + * disassociate the STA instead. */ + sta->disassoc_reason = reason; + sta->flags |= WLAN_STA_PENDING_DISASSOC_CB; + eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); + eloop_register_timeout(hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? + 2 : 0, 0, ap_sta_disassoc_cb_timeout, + hapd, sta); + return; + } + sta->deauth_reason = reason; sta->flags |= WLAN_STA_PENDING_DEAUTH_CB; eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); @@ -1275,6 +1337,15 @@ void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd, "%s: Removed ap_sta_disassoc_cb_timeout timeout for " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); + if (eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta) > 0) + { + wpa_printf(MSG_DEBUG, + "%s: Removed ap_sta_delayed_1x_auth_fail_cb timeout for " + MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + if (sta->flags & WLAN_STA_WPS) + hostapd_wps_eap_completed(hapd); + } } @@ -1283,7 +1354,7 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) int res; buf[0] = '\0'; - res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", (flags & WLAN_STA_AUTH ? "[AUTH]" : ""), (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""), (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""), @@ -1300,6 +1371,7 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) (flags & WLAN_STA_NONERP ? "[NonERP]" : ""), (flags & WLAN_STA_WPS2 ? "[WPS2]" : ""), (flags & WLAN_STA_GAS ? "[GAS]" : ""), + (flags & WLAN_STA_HT ? "[HT]" : ""), (flags & WLAN_STA_VHT ? "[VHT]" : ""), (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""), (flags & WLAN_STA_WNM_SLEEP_MODE ? @@ -1309,3 +1381,48 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) return res; } + + +static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + u16 reason; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "IEEE 802.1X: Scheduled disconnection of " MACSTR + " after EAP-Failure", MAC2STR(sta->addr)); + + reason = sta->disconnect_reason_code; + if (!reason) + reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED; + ap_sta_disconnect(hapd, sta, sta->addr, reason); + if (sta->flags & WLAN_STA_WPS) + hostapd_wps_eap_completed(hapd); +} + + +void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, + struct sta_info *sta) +{ + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "IEEE 802.1X: Force disconnection of " MACSTR + " after EAP-Failure in 10 ms", MAC2STR(sta->addr)); + + /* + * Add a small sleep to increase likelihood of previously requested + * EAP-Failure TX getting out before this should the driver reorder + * operations. + */ + eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta); + eloop_register_timeout(0, 10000, ap_sta_delayed_1x_auth_fail_cb, + hapd, sta); +} + + +int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, + struct sta_info *sta) +{ + return eloop_is_timeout_registered(ap_sta_delayed_1x_auth_fail_cb, + hapd, sta); +} diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 099de62d1a9a..9cac6f157f68 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -1,6 +1,6 @@ /* * hostapd / Station table - * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,14 +9,11 @@ #ifndef STA_INFO_H #define STA_INFO_H -#ifdef CONFIG_MESH -/* needed for mesh_plink_state enum */ #include "common/defs.h" -#include "common/wpa_common.h" -#endif /* CONFIG_MESH */ - #include "list.h" #include "vlan.h" +#include "common/wpa_common.h" +#include "common/ieee802_11_defs.h" /* STA flags */ #define WLAN_STA_AUTH BIT(0) @@ -38,6 +35,7 @@ #define WLAN_STA_WNM_SLEEP_MODE BIT(19) #define WLAN_STA_VHT_OPMODE_ENABLED BIT(20) #define WLAN_STA_VENDOR_VHT BIT(21) +#define WLAN_STA_PENDING_FILS_ERP BIT(22) #define WLAN_STA_PENDING_DISASSOC_CB BIT(29) #define WLAN_STA_PENDING_DEAUTH_CB BIT(30) #define WLAN_STA_NONERP BIT(31) @@ -46,6 +44,7 @@ * Supported Rates IEs). */ #define WLAN_SUPP_RATES_MAX 32 +struct hostapd_data; struct mbo_non_pref_chan_info { struct mbo_non_pref_chan_info *next; @@ -68,6 +67,7 @@ struct sta_info { be32 ipaddr; struct dl_list ip6addr; /* list head for struct ip6addr */ u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */ + u16 disconnect_reason_code; /* RADIUS server override */ u32 flags; /* Bitfield of WLAN_STA_* */ u16 capability; u16 listen_interval; /* or beacon_int for APs */ @@ -113,6 +113,10 @@ struct sta_info { unsigned int radius_das_match:1; unsigned int ecsa_supported:1; unsigned int added_unassoc:1; + unsigned int pending_wds_enable:1; + unsigned int power_capab:1; + unsigned int agreed_to_steer:1; + unsigned int hs20_t_c_filtering:1; u16 auth_alg; @@ -170,17 +174,20 @@ struct sta_info { struct os_reltime sa_query_start; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_INTERWORKING +#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP) #define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */ struct gas_dialog_info *gas_dialog; u8 gas_dialog_next; -#endif /* CONFIG_INTERWORKING */ +#endif /* CONFIG_INTERWORKING || CONFIG_DPP */ struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */ struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */ struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */ + /* Hotspot 2.0 Roaming Consortium from (Re)Association Request */ + struct wpabuf *roaming_consortium; u8 remediation_method; char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */ + char *t_c_url; /* HS 2.0 Terms and Conditions Server URL */ struct wpabuf *hs20_deauth_req; char *hs20_session_info_url; int hs20_disassoc_timer; @@ -195,7 +202,8 @@ struct sta_info { unsigned int mesh_sae_pmksa_caching:1; #endif /* CONFIG_SAE */ - u32 session_timeout; /* valid only if session_timeout_set == 1 */ + /* valid only if session_timeout_set == 1 */ + struct os_reltime session_timeout; /* Last Authentication/(Re)Association Request/Action frame sequence * control */ @@ -214,10 +222,51 @@ struct sta_info { u8 rrm_enabled_capa[5]; + s8 min_tx_power; + s8 max_tx_power; + #ifdef CONFIG_TAXONOMY struct wpabuf *probe_ie_taxonomy; struct wpabuf *assoc_ie_taxonomy; #endif /* CONFIG_TAXONOMY */ + +#ifdef CONFIG_FILS + u8 fils_snonce[FILS_NONCE_LEN]; + u8 fils_session[FILS_SESSION_LEN]; + u8 fils_erp_pmkid[PMKID_LEN]; + u8 *fils_pending_assoc_req; + size_t fils_pending_assoc_req_len; + unsigned int fils_pending_assoc_is_reassoc:1; + unsigned int fils_dhcp_rapid_commit_proxy:1; + unsigned int fils_erp_pmkid_set:1; + unsigned int fils_drv_assoc_finish:1; + struct wpabuf *fils_hlp_resp; + struct wpabuf *hlp_dhcp_discover; + void (*fils_pending_cb)(struct hostapd_data *hapd, struct sta_info *sta, + u16 resp, struct wpabuf *data, int pub); +#ifdef CONFIG_FILS_SK_PFS + struct crypto_ecdh *fils_ecdh; +#endif /* CONFIG_FILS_SK_PFS */ + struct wpabuf *fils_dh_ss; + struct wpabuf *fils_g_sta; +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + u8 *owe_pmk; + size_t owe_pmk_len; + struct crypto_ecdh *owe_ecdh; + u16 owe_group; +#endif /* CONFIG_OWE */ + + u8 *ext_capability; + char *ifname_wds; /* WDS ifname, if in use */ + +#ifdef CONFIG_TESTING_OPTIONS + enum wpa_alg last_tk_alg; + int last_tk_key_idx; + u8 last_tk[WPA_TK_MAX_LEN]; + size_t last_tk_len; +#endif /* CONFIG_TESTING_OPTIONS */ }; @@ -237,8 +286,6 @@ struct sta_info { #define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5) -struct hostapd_data; - int ap_for_each_sta(struct hostapd_data *hapd, int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, void *ctx), @@ -289,5 +336,9 @@ void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd, struct sta_info *sta); int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen); +void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, + struct sta_info *sta); +int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, + struct sta_info *sta); #endif /* STA_INFO_H */ diff --git a/src/ap/taxonomy.c b/src/ap/taxonomy.c index cea8b726f47a..ae157a7c91d6 100644 --- a/src/ap/taxonomy.c +++ b/src/ap/taxonomy.c @@ -21,6 +21,7 @@ #include "common/wpa_ctrl.h" #include "hostapd.h" #include "sta_info.h" +#include "taxonomy.h" /* Copy a string with no funny schtuff allowed; only alphanumerics. */ diff --git a/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c index 4725e2b3e8f1..557570cd1bc4 100644 --- a/src/ap/tkip_countermeasures.c +++ b/src/ap/tkip_countermeasures.c @@ -71,6 +71,11 @@ int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local) struct os_reltime now; int ret = 0; + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Michael MIC failure detected in received frame%s", + local ? " (local)" : ""); + if (addr && local) { struct sta_info *sta = ap_get_sta(hapd, addr); if (sta != NULL) { diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c index 31e4fc6b396a..01fecee026a1 100644 --- a/src/ap/vlan_init.c +++ b/src/ap/vlan_init.c @@ -138,6 +138,8 @@ int vlan_init(struct hostapd_data *hapd) !hapd->conf->vlan) { /* dynamic vlans enabled but no (or empty) vlan_file given */ struct hostapd_vlan *vlan; + int ret; + vlan = os_zalloc(sizeof(*vlan)); if (vlan == NULL) { wpa_printf(MSG_ERROR, "Out of memory while assigning " @@ -146,8 +148,16 @@ int vlan_init(struct hostapd_data *hapd) } vlan->vlan_id = VLAN_ID_WILDCARD; - os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#", - hapd->conf->iface); + ret = os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#", + hapd->conf->iface); + if (ret >= (int) sizeof(vlan->ifname)) { + wpa_printf(MSG_WARNING, + "VLAN: Interface name was truncated to %s", + vlan->ifname); + } else if (ret < 0) { + os_free(vlan); + return ret; + } vlan->next = hapd->conf->vlan; hapd->conf->vlan = vlan; } diff --git a/src/ap/wmm.c b/src/ap/wmm.c index 314e244bc956..8054c5d2f243 100644 --- a/src/ap/wmm.c +++ b/src/ap/wmm.c @@ -21,11 +21,6 @@ #include "wmm.h" -/* TODO: maintain separate sequence and fragment numbers for each AC - * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA - * if only WMM stations are receiving a certain group */ - - static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci) { u8 ret; @@ -157,8 +152,9 @@ static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr, int wmm_process_tspec(struct wmm_tspec_element *tspec) { - int medium_time, pps, duration; - int up, psb, dir, tid; + u64 medium_time; + unsigned int pps, duration; + unsigned int up, psb, dir, tid; u16 val, surplus; up = (tspec->ts_info[1] >> 3) & 0x07; @@ -206,8 +202,9 @@ int wmm_process_tspec(struct wmm_tspec_element *tspec) return WMM_ADDTS_STATUS_INVALID_PARAMETERS; } - medium_time = surplus * pps * duration / 0x2000; - wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time); + medium_time = (u64) surplus * pps * duration / 0x2000; + wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %lu", + (unsigned long) medium_time); /* * TODO: store list of granted (and still active) TSPECs and check diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c index 41d50cebfbe0..1e8f58b4d07e 100644 --- a/src/ap/wnm_ap.c +++ b/src/ap/wnm_ap.c @@ -95,8 +95,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, if (mgmt == NULL) { wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " "WNM-Sleep Response action frame"); - os_free(wnmtfs_ie); - return -1; + res = -1; + goto fail; } os_memcpy(mgmt->da, addr, ETH_ALEN); os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); @@ -109,6 +109,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, 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) || + hapd->conf->wnm_sleep_mode_no_keys || action_type != WNM_SLEEP_MODE_EXIT) { mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0; } else { @@ -118,11 +119,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, (int) gtk_elem_len); #ifdef CONFIG_IEEE80211W res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos); - if (res < 0) { - os_free(wnmtfs_ie); - os_free(mgmt); - return -1; - } + if (res < 0) + goto fail; igtk_elem_len = res; pos += igtk_elem_len; wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d", @@ -176,7 +174,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, 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)) + if (!wpa_auth_uses_mfp(sta->wpa_sm) || + hapd->conf->wnm_sleep_mode_no_keys) wpa_wnmsleep_rekey_gtk(sta->wpa_sm); } } else @@ -184,6 +183,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, #undef MAX_GTK_SUBELEM_LEN #undef MAX_IGTK_SUBELEM_LEN +fail: os_free(wnmtfs_ie); os_free(mgmt); return res; @@ -202,12 +202,20 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, u8 *tfsreq_ie_end = NULL; u16 tfsreq_ie_len = 0; + if (!hapd->conf->wnm_sleep_mode) { + wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from " + MACSTR " since WNM-Sleep Mode is disabled", + MAC2STR(addr)); + return; + } + 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) + if (*pos == WLAN_EID_WNMSLEEP && + ie_len >= (int) sizeof(*wnmsleep_ie) - 2) wnmsleep_ie = (struct wnm_sleep_element *) pos; else if (*pos == WLAN_EID_TFS_REQ) { if (!tfsreq_ie_start) @@ -251,20 +259,14 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd, const u8 *addr, - u8 dialog_token, - const char *url) + u8 dialog_token) { struct ieee80211_mgmt *mgmt; - size_t url_len, len; + size_t len; u8 *pos; int res; - if (url) - url_len = os_strlen(url); - else - url_len = 0; - - mgmt = os_zalloc(sizeof(*mgmt) + (url_len ? 1 + url_len : 0)); + mgmt = os_zalloc(sizeof(*mgmt)); if (mgmt == NULL) return -1; os_memcpy(mgmt->da, addr, ETH_ALEN); @@ -279,11 +281,6 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd, mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0); mgmt->u.action.u.bss_tm_req.validity_interval = 1; pos = mgmt->u.action.u.bss_tm_req.variable; - if (url) { - *pos++ += url_len; - os_memcpy(pos, url, url_len); - pos += url_len; - } wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u " @@ -307,6 +304,20 @@ static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd, { u8 dialog_token, reason; const u8 *pos, *end; + int enabled = hapd->conf->bss_transition; + +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled) + enabled = 1; +#endif /* CONFIG_MBO */ + if (!enabled) { + wpa_printf(MSG_DEBUG, + "Ignore BSS Transition Management Query from " + MACSTR + " since BSS Transition Management is disabled", + MAC2STR(addr)); + return; + } if (len < 2) { wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from " @@ -326,7 +337,20 @@ static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd, wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries", pos, end - pos); - ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token, NULL); + ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token); +} + + +void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + + if (sta->agreed_to_steer) { + wpa_printf(MSG_DEBUG, "%s: Reset steering flag for STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + sta->agreed_to_steer = 0; + } } @@ -336,6 +360,21 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd, { u8 dialog_token, status_code, bss_termination_delay; const u8 *pos, *end; + int enabled = hapd->conf->bss_transition; + struct sta_info *sta; + +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled) + enabled = 1; +#endif /* CONFIG_MBO */ + if (!enabled) { + wpa_printf(MSG_DEBUG, + "Ignore BSS Transition Management Response from " + MACSTR + " since BSS Transition Management is disabled", + MAC2STR(addr)); + return; + } if (len < 3) { wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from " @@ -354,11 +393,23 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd, "bss_termination_delay=%u", MAC2STR(addr), dialog_token, status_code, bss_termination_delay); + sta = ap_get_sta(hapd, addr); + if (!sta) { + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not found for received BSS TM Response", + MAC2STR(addr)); + return; + } + if (status_code == WNM_BSS_TM_ACCEPT) { if (end - pos < ETH_ALEN) { wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field"); return; } + sta->agreed_to_steer = 1; + eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta); + eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer, + hapd, sta); wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR, MAC2STR(pos)); wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR @@ -368,6 +419,7 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd, MAC2STR(pos)); pos += ETH_ALEN; } else { + sta->agreed_to_steer = 0; wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR " status_code=%u bss_termination_delay=%u", MAC2STR(addr), status_code, bss_termination_delay); @@ -401,6 +453,48 @@ static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd, } +static void ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data *hapd, + const u8 *addr, const u8 *buf, + size_t len) +{ + u8 dialog_token; + char *hex; + size_t hex_len; + + if (!hapd->conf->coloc_intf_reporting) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore unexpected Collocated Interference Report from " + MACSTR, MAC2STR(addr)); + return; + } + + if (len < 1) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore too short Collocated Interference Report from " + MACSTR, MAC2STR(addr)); + return; + } + dialog_token = *buf++; + len--; + + wpa_printf(MSG_DEBUG, + "WNM: Received Collocated Interference Report frame from " + MACSTR " (dialog_token=%u)", + MAC2STR(addr), dialog_token); + wpa_hexdump(MSG_MSGDUMP, "WNM: Collocated Interference Report Elements", + buf, len); + + hex_len = 2 * len + 1; + hex = os_malloc(hex_len); + if (!hex) + return; + wpa_snprintf_hex(hex, hex_len, buf, len); + wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, COLOC_INTF_REPORT MACSTR " %d %s", + MAC2STR(addr), dialog_token, hex); + os_free(hex); +} + + int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { @@ -431,6 +525,10 @@ int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload, plen); return 0; + case WNM_COLLOCATED_INTERFERENCE_REPORT: + ieee802_11_rx_wnm_coloc_intf_report(hapd, mgmt->sa, payload, + plen); + return 0; } wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR, @@ -629,3 +727,40 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, return 0; } + + +int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta, + unsigned int auto_report, unsigned int timeout) +{ + u8 buf[100], *pos; + struct ieee80211_mgmt *mgmt; + u8 dialog_token = 1; + + if (auto_report > 3 || timeout > 63) + return -1; + os_memset(buf, 0, sizeof(buf)); + mgmt = (struct ieee80211_mgmt *) buf; + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt->da, sta->addr, ETH_ALEN); + os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); + mgmt->u.action.category = WLAN_ACTION_WNM; + mgmt->u.action.u.coloc_intf_req.action = + WNM_COLLOCATED_INTERFERENCE_REQ; + mgmt->u.action.u.coloc_intf_req.dialog_token = dialog_token; + mgmt->u.action.u.coloc_intf_req.req_info = auto_report | (timeout << 2); + pos = &mgmt->u.action.u.coloc_intf_req.req_info; + pos++; + + wpa_printf(MSG_DEBUG, "WNM: Sending Collocated Interference Request to " + MACSTR " (dialog_token=%u auto_report=%u timeout=%u)", + MAC2STR(sta->addr), dialog_token, auto_report, timeout); + if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { + wpa_printf(MSG_DEBUG, + "WNM: Failed to send Collocated Interference Request frame"); + return -1; + } + + return 0; +} diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h index a44eadb85e55..1806ba0e0525 100644 --- a/src/ap/wnm_ap.h +++ b/src/ap/wnm_ap.h @@ -23,5 +23,8 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, const u8 *bss_term_dur, const char *url, const u8 *nei_rep, size_t nei_rep_len, const u8 *mbo_attrs, size_t mbo_len); +void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx); +int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta, + unsigned int auto_report, unsigned int timeout); #endif /* WNM_AP_H */ diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index bf10cc1646f7..34969e79e46f 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -1,6 +1,6 @@ /* * IEEE 802.11 RSN / WPA Authenticator - * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -13,10 +13,13 @@ #include "utils/state_machine.h" #include "utils/bitfield.h" #include "common/ieee802_11_defs.h" +#include "crypto/aes.h" #include "crypto/aes_wrap.h" +#include "crypto/aes_siv.h" #include "crypto/crypto.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/sha384.h" #include "crypto/random.h" #include "eapol_auth/eapol_auth_sm.h" #include "ap_config.h" @@ -33,8 +36,14 @@ 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 int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK, + u8 *data, size_t data_len); +#ifdef CONFIG_FILS +static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk, + u8 *buf, size_t buf_len, u16 *_key_data_len); +static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm, + const struct wpabuf *hlp); +#endif /* CONFIG_FILS */ 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); @@ -52,12 +61,12 @@ 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 u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos); -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 */ +static const u32 eapol_key_timeout_no_retrans = 4000; /* ms */ /* TODO: make these configurable */ static const int dot11RSNAConfigPMKLifetime = 43200; @@ -68,8 +77,8 @@ 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); + if (wpa_auth->cb->mic_failure_report) + return wpa_auth->cb->mic_failure_report(wpa_auth->cb_ctx, addr); return 0; } @@ -77,8 +86,8 @@ static inline int wpa_auth_mic_failure_report( 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); + if (wpa_auth->cb->psk_failure_report) + wpa_auth->cb->psk_failure_report(wpa_auth->cb_ctx, addr); } @@ -86,38 +95,38 @@ 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); + 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) + if (wpa_auth->cb->get_eapol == NULL) return -1; - return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var); + 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) + const u8 *prev_psk, size_t *psk_len) { - if (wpa_auth->cb.get_psk == NULL) + 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); + return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr, + prev_psk, psk_len); } 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) + if (wpa_auth->cb->get_msk == NULL) return -1; - return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len); + return wpa_auth->cb->get_msk(wpa_auth->cb_ctx, addr, msk, len); } @@ -126,19 +135,19 @@ static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len) { - if (wpa_auth->cb.set_key == NULL) + 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); + 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) + if (wpa_auth->cb->get_seqnum == NULL) return -1; - return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); + return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq); } @@ -146,10 +155,10 @@ 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) + if (wpa_auth->cb->send_eapol == NULL) return -1; - return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len, - encrypt); + return wpa_auth->cb->send_eapol(wpa_auth->cb_ctx, addr, data, data_len, + encrypt); } @@ -157,9 +166,9 @@ wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth, const u8 *addr) { - if (wpa_auth->cb.start_ampe == NULL) + if (wpa_auth->cb->start_ampe == NULL) return -1; - return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr); + return wpa_auth->cb->start_ampe(wpa_auth->cb_ctx, addr); } #endif /* CONFIG_MESH */ @@ -168,9 +177,9 @@ 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) + if (wpa_auth->cb->for_each_sta == NULL) return 0; - return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx); + return wpa_auth->cb->for_each_sta(wpa_auth->cb_ctx, cb, cb_ctx); } @@ -178,18 +187,18 @@ 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) + if (wpa_auth->cb->for_each_auth == NULL) return 0; - return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx); + 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) + if (wpa_auth->cb->logger == NULL) return; - wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt); + wpa_auth->cb->logger(wpa_auth->cb_ctx, addr, level, txt); } @@ -200,7 +209,7 @@ void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr, int maxlen; va_list ap; - if (wpa_auth->cb.logger == NULL) + if (wpa_auth->cb->logger == NULL) return; maxlen = os_strlen(fmt) + 100; @@ -219,30 +228,13 @@ void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr, static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, - const u8 *addr) + const u8 *addr, u16 reason) { - if (wpa_auth->cb.disconnect == NULL) + 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; + wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR " (reason %u)", + MAC2STR(addr), reason); + wpa_auth->cb->disconnect(wpa_auth->cb_ctx, addr, reason); } @@ -409,7 +401,8 @@ static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, */ struct wpa_authenticator * wpa_init(const u8 *addr, struct wpa_auth_config *conf, - struct wpa_auth_callbacks *cb) + const struct wpa_auth_callbacks *cb, + void *cb_ctx) { struct wpa_authenticator *wpa_auth; @@ -418,7 +411,8 @@ struct wpa_authenticator * wpa_init(const u8 *addr, 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)); + wpa_auth->cb = cb; + wpa_auth->cb_ctx = cb_ctx; if (wpa_auth_gen_wpa_ie(wpa_auth)) { wpa_printf(MSG_ERROR, "Could not generate WPA IE."); @@ -443,7 +437,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr, return NULL; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP 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."); @@ -453,7 +447,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr, os_free(wpa_auth); return NULL; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (wpa_auth->conf.wpa_gmk_rekey) { eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, @@ -506,17 +500,13 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth) 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 +#ifdef CONFIG_IEEE80211R_AP wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache); wpa_auth->ft_pmk_cache = NULL; -#endif /* CONFIG_IEEE80211R */ + wpa_ft_deinit(wpa_auth); +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_P2P bitfield_free(wpa_auth->ip_pool); @@ -599,16 +589,28 @@ int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return -1; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP 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; + sm->Pair = TRUE; return 0; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ + +#ifdef CONFIG_FILS + if (sm->fils_completed) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "FILS authentication already completed - do not start 4-way handshake"); + /* Go to PTKINITDONE state to allow GTK rekeying */ + sm->wpa_ptk_state = WPA_PTK_PTKINITDONE; + sm->Pair = TRUE; + return 0; + } +#endif /* CONFIG_FILS */ if (sm->started) { os_memset(&sm->key_replay, 0, sizeof(sm->key_replay)); @@ -660,10 +662,10 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm) sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP os_free(sm->assoc_resp_ftie); wpabuf_free(sm->ft_pending_req_ies); -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ os_free(sm->last_rx_eapol_key); os_free(sm->wpa_ie); wpa_group_put(sm->wpa_auth, sm->group); @@ -680,15 +682,19 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm) 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); + if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk, + sm->wpa_auth, NULL) == -1) + 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); +#ifdef CONFIG_IEEE80211R_AP + wpa_ft_sta_deinit(sm); +#endif /* CONFIG_IEEE80211R_AP */ 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(). */ @@ -739,7 +745,7 @@ static void wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter *ctr, } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_ie_parse *kde) @@ -786,7 +792,7 @@ static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth, return 0; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth, @@ -828,29 +834,38 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, struct wpa_ptk PTK; int ok = 0; const u8 *pmk = NULL; - unsigned int pmk_len; + size_t pmk_len; + os_memset(&PTK, 0, sizeof(PTK)); for (;;) { - if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && + !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, - sm->p2p_dev_addr, pmk); + sm->p2p_dev_addr, pmk, &pmk_len); if (pmk == NULL) break; - pmk_len = PMK_LEN; +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) { + os_memcpy(sm->xxkey, pmk, pmk_len); + sm->xxkey_len = pmk_len; + } +#endif /* CONFIG_IEEE80211R_AP */ } else { pmk = sm->PMK; pmk_len = sm->pmk_len; } - wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK); + if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK) < 0) + break; - if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len) - == 0) { + if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK, + data, data_len) == 0) { ok = 1; break; } - if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) + if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || + wpa_key_mgmt_sae(sm->wpa_key_mgmt)) break; } @@ -877,39 +892,41 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, { 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; + enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } 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; + const u8 *key_data; + size_t keyhdrlen, mic_len; + u8 *mic; if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return; + wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len); - mic_len = wpa_mic_len(sm->wpa_key_mgmt); - keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); + mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); + keyhdrlen = sizeof(*key) + mic_len + 2; - if (data_len < sizeof(*hdr) + keyhdrlen) + if (data_len < sizeof(*hdr) + keyhdrlen) { + wpa_printf(MSG_DEBUG, "WPA: Ignore too short EAPOL-Key frame"); return; + } hdr = (struct ieee802_1x_hdr *) data; key = (struct wpa_eapol_key *) (hdr + 1); - key192 = (struct wpa_eapol_key_192 *) (hdr + 1); + mic = (u8 *) (key + 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); - } + key_data = mic + mic_len + 2; + key_data_length = WPA_GET_BE16(mic + mic_len); 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); + " key_info=0x%x type=%u mic_len=%u key_data_length=%u", + MAC2STR(sm->addr), key_info, key->type, + (unsigned int) mic_len, key_data_length); + wpa_hexdump(MSG_MSGDUMP, + "WPA: EAPOL-Key header (ending before Key MIC)", + key, sizeof(*key)); + wpa_hexdump(MSG_MSGDUMP, "WPA: EAPOL-Key Key MIC", + mic, mic_len); if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) { wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - " "key_data overflow (%d > %lu)", @@ -950,25 +967,20 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, /* 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) { + if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { + wpa_printf(MSG_DEBUG, "WPA: Ignore SMK message"); + return; + } + + 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) { + } else if (key_data_length == 0 || + (mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && + key_data_length == AES_BLOCK_SIZE)) { msg = PAIRWISE_4; msgtxt = "4/4 Pairwise"; } else { @@ -976,15 +988,13 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, 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) && + if (wpa_use_cmac(sm->wpa_key_mgmt) && + !wpa_use_akm_defined(sm->wpa_key_mgmt) && ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, @@ -994,7 +1004,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, return; } - if (!wpa_use_aes_cmac(sm) && + if (!wpa_use_cmac(sm->wpa_key_mgmt) && + !wpa_use_akm_defined(sm->wpa_key_mgmt) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, @@ -1004,7 +1015,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } } - if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) && + if (wpa_use_akm_defined(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"); @@ -1092,6 +1103,15 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } continue_processing: +#ifdef CONFIG_FILS + if (sm->wpa == WPA_VERSION_WPA2 && mic_len == 0 && + !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "WPA: Encr Key Data bit not set even though AEAD cipher is supposed to be used - drop frame"); + return; + } +#endif /* CONFIG_FILS */ + switch (msg) { case PAIRWISE_2: if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && @@ -1119,70 +1139,10 @@ continue_processing: "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"); + wpa_sta_disconnect(wpa_auth, sm->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); 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 || @@ -1204,28 +1164,6 @@ continue_processing: 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; } @@ -1239,22 +1177,42 @@ continue_processing: return; } - if (!(key_info & WPA_KEY_INFO_MIC)) { + if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt) && + !(key_info & WPA_KEY_INFO_MIC)) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received invalid EAPOL-Key: Key MIC not set"); return; } +#ifdef CONFIG_FILS + if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) && + (key_info & WPA_KEY_INFO_MIC)) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received invalid EAPOL-Key: Key MIC set"); + return; + } +#endif /* CONFIG_FILS */ + sm->MICVerified = FALSE; if (sm->PTK_valid && !sm->update_snonce) { - if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data, - data_len) && + if (mic_len && + wpa_verify_key_mic(sm->wpa_key_mgmt, sm->pmk_len, &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; } +#ifdef CONFIG_FILS + if (!mic_len && + wpa_aead_decrypt(sm, &sm->PTK, data, data_len, + &key_data_length) < 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key with invalid MIC"); + return; + } +#endif /* CONFIG_FILS */ sm->MICVerified = TRUE; eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); sm->pending_1_of_4_timeout = 0; @@ -1277,12 +1235,7 @@ continue_processing: * 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 (key_info & WPA_KEY_INFO_ERROR) { if (wpa_receive_error_report( wpa_auth, sm, !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0) @@ -1292,11 +1245,6 @@ continue_processing: "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 && @@ -1335,18 +1283,10 @@ continue_processing: 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); + sm->last_rx_eapol_key = os_memdup(data, 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); @@ -1361,7 +1301,7 @@ continue_processing: 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 data[ETH_ALEN + WPA_NONCE_LEN + 8 + WPA_GTK_MAX_LEN]; u8 *pos; int ret = 0; @@ -1372,21 +1312,30 @@ static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr, * is done only at the Authenticator and as such, does not need to be * exactly same. */ + os_memset(data, 0, sizeof(data)); 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) + if (random_get_bytes(pos, gtk_len) < 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) +#ifdef CONFIG_SHA384 + if (sha384_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), + gtk, gtk_len) < 0) ret = -1; -#endif /* CONFIG_IEEE80211W */ +#else /* CONFIG_SHA384 */ +#ifdef CONFIG_SHA256 + if (sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), + gtk, gtk_len) < 0) + ret = -1; +#else /* CONFIG_SHA256 */ + if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), + gtk, gtk_len) < 0) + ret = -1; +#endif /* CONFIG_SHA256 */ +#endif /* CONFIG_SHA384 */ return ret; } @@ -1412,26 +1361,24 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, { 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; + u8 *key_mic, *key_data; - mic_len = wpa_mic_len(sm->wpa_key_mgmt); - keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); + mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); + keyhdrlen = sizeof(*key) + mic_len + 2; 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)) + else if (wpa_use_akm_defined(sm->wpa_key_mgmt)) version = WPA_KEY_INFO_TYPE_AKM_DEFINED; - else if (wpa_use_aes_cmac(sm)) + else if (wpa_use_cmac(sm->wpa_key_mgmt)) version = WPA_KEY_INFO_TYPE_AES_128_CMAC; else if (sm->pairwise != WPA_CIPHER_TKIP) version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; @@ -1453,8 +1400,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, 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) || + wpa_use_aes_key_wrap(sm->wpa_key_mgmt) || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) { pad_len = key_data_len % 8; if (pad_len) @@ -1463,6 +1409,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, } len += key_data_len; + if (!mic_len && encr) + len += AES_BLOCK_SIZE; hdr = os_zalloc(len); if (hdr == NULL) @@ -1471,7 +1419,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, 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_mic = (u8 *) (key + 1); key_data = ((u8 *) (hdr + 1)) + keyhdrlen; key->type = sm->wpa == WPA_VERSION_WPA2 ? @@ -1484,11 +1432,11 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, 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) + if (sm->wpa == WPA_VERSION_WPA2 && !pairwise) WPA_PUT_BE16(key->key_length, 0); + else + WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg)); - /* 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, @@ -1510,10 +1458,31 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, 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); + WPA_PUT_BE16(key_mic + mic_len, kde_len); +#ifdef CONFIG_FILS + } else if (!mic_len && kde) { + const u8 *aad[1]; + size_t aad_len[1]; + + WPA_PUT_BE16(key_mic, AES_BLOCK_SIZE + kde_len); + wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", + kde, kde_len); + + wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", + sm->PTK.kek, sm->PTK.kek_len); + /* AES-SIV AAD from EAPOL protocol version field (inclusive) to + * to Key Data (exclusive). */ + aad[0] = (u8 *) hdr; + aad_len[0] = key_mic + 2 - (u8 *) hdr; + if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, kde, kde_len, + 1, aad, aad_len, key_mic + 2) < 0) { + wpa_printf(MSG_DEBUG, "WPA: AES-SIV encryption failed"); + return; + } + + wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV", + key_mic + 2, AES_BLOCK_SIZE + kde_len); +#endif /* CONFIG_FILS */ } else if (encr && kde) { buf = os_zalloc(key_data_len); if (buf == NULL) { @@ -1530,24 +1499,24 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, 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) || + wpa_use_aes_key_wrap(sm->wpa_key_mgmt) || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) { + wpa_printf(MSG_DEBUG, + "WPA: Encrypt Key Data using AES-WRAP (KEK length %u)", + (unsigned int) sm->PTK.kek_len); 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); + WPA_PUT_BE16(key_mic + mic_len, key_data_len); #ifndef CONFIG_NO_RC4 } else if (sm->PTK.kek_len == 16) { u8 ek[32]; + + wpa_printf(MSG_DEBUG, + "WPA: Encrypt Key Data using RC4"); os_memcpy(key->key_iv, sm->group->Counter + WPA_NONCE_LEN - 16, 16); inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); @@ -1555,12 +1524,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, 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); + WPA_PUT_BE16(key_mic + mic_len, key_data_len); #endif /* CONFIG_NO_RC4 */ } else { os_free(hdr); @@ -1571,9 +1535,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, } if (key_info & WPA_KEY_INFO_MIC) { - u8 *key_mic; - - if (!sm->PTK_valid) { + if (!sm->PTK_valid || !mic_len) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "PTK not valid when sending EAPOL-Key " "frame"); @@ -1581,10 +1543,12 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, 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); + if (wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len, + sm->wpa_key_mgmt, version, + (u8 *) hdr, len, key_mic) < 0) { + os_free(hdr); + return; + } #ifdef CONFIG_TESTING_OPTIONS if (!pairwise && wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 && @@ -1613,7 +1577,7 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, { int timeout_ms; int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; - int ctr; + u32 ctr; if (sm == NULL) return; @@ -1627,41 +1591,43 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, eapol_key_timeout_first_group; else timeout_ms = eapol_key_timeout_subseq; + if (wpa_auth->conf.wpa_disable_eapol_key_retries && + (!pairwise || (key_info & WPA_KEY_INFO_MIC))) + timeout_ms = eapol_key_timeout_no_retrans; 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); + "counter %u)", 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) +static int wpa_verify_key_mic(int akmp, size_t pmk_len, 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); + u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos; + size_t mic_len = wpa_mic_len(akmp, pmk_len); 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); + mic_pos = (u8 *) (key + 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); + os_memcpy(mic, mic_pos, mic_len); + os_memset(mic_pos, 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) + data, data_len, mic_pos) || + os_memcmp_const(mic, mic_pos, mic_len) != 0) ret = -1; - os_memcpy(key192->key_mic, mic, mic_len); + os_memcpy(mic_pos, mic, mic_len); return ret; } @@ -1670,7 +1636,10 @@ 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); + if (wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, + 0)) + wpa_printf(MSG_DEBUG, + "RSN: PTK removal from the driver failed"); sm->pairwise_set = FALSE; eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); } @@ -1734,7 +1703,7 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) sm->ReAuthenticationRequest = TRUE; break; case WPA_ASSOC_FT: -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration " "after association"); wpa_ft_install_ptk(sm); @@ -1742,22 +1711,37 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) /* Using FT protocol, not WPA auth state machine */ sm->ft_completed = 1; return 0; -#else /* CONFIG_IEEE80211R */ +#else /* CONFIG_IEEE80211R_AP */ break; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ + case WPA_ASSOC_FILS: +#ifdef CONFIG_FILS + wpa_printf(MSG_DEBUG, + "FILS: TK configuration after association"); + fils_set_tk(sm); + sm->fils_completed = 1; + return 0; +#else /* CONFIG_FILS */ + break; +#endif /* CONFIG_FILS */ case WPA_DRV_STA_REMOVED: sm->tk_already_set = FALSE; return 0; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP sm->ft_completed = 0; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W if (sm->mgmt_frame_prot && event == WPA_AUTH) remove_ptk = 0; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_FILS + if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) && + (event == WPA_AUTH || event == WPA_ASSOC)) + remove_ptk = 0; +#endif /* CONFIG_FILS */ if (remove_ptk) { sm->PTK_valid = FALSE; @@ -1802,7 +1786,9 @@ SM_STATE(WPA_PTK, INITIALIZE) 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)) { + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || + sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) { wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_authorized, 0); } @@ -1811,9 +1797,14 @@ SM_STATE(WPA_PTK, INITIALIZE) SM_STATE(WPA_PTK, DISCONNECT) { + u16 reason = sm->disconnect_reason; + SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk); sm->Disconnect = FALSE; - wpa_sta_disconnect(sm->wpa_auth, sm->addr); + sm->disconnect_reason = 0; + if (!reason) + reason = WLAN_REASON_PREV_AUTH_NOT_VALID; + wpa_sta_disconnect(sm->wpa_auth, sm->addr, reason); } @@ -1922,17 +1913,25 @@ SM_STATE(WPA_PTK, INITPMK) size_t len = 2 * PMK_LEN; SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP sm->xxkey_len = 0; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ 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; +#ifdef CONFIG_DPP + } else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) { + wpa_printf(MSG_DEBUG, + "DPP: No PMKSA cache entry for STA - reject connection"); + sm->Disconnect = TRUE; + sm->disconnect_reason = WLAN_REASON_INVALID_PMKID; + return; +#endif /* CONFIG_DPP */ } 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) + if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) pmk_len = PMK_LEN_SUITE_B_192; else pmk_len = PMK_LEN; @@ -1948,15 +1947,20 @@ SM_STATE(WPA_PTK, INITPMK) } os_memcpy(sm->PMK, msk, pmk_len); sm->pmk_len = pmk_len; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (len >= 2 * PMK_LEN) { - os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); - sm->xxkey_len = PMK_LEN; + if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) { + os_memcpy(sm->xxkey, msk, SHA384_MAC_LEN); + sm->xxkey_len = SHA384_MAC_LEN; + } else { + os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); + sm->xxkey_len = PMK_LEN; + } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ } else { wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p", - sm->wpa_auth->cb.get_msk); + sm->wpa_auth->cb->get_msk); sm->Disconnect = TRUE; return; } @@ -1978,16 +1982,26 @@ SM_STATE(WPA_PTK, INITPMK) SM_STATE(WPA_PTK, INITPSK) { const u8 *psk; + size_t psk_len; + SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); - psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL); + psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL, + &psk_len); if (psk) { - os_memcpy(sm->PMK, psk, PMK_LEN); - sm->pmk_len = PMK_LEN; -#ifdef CONFIG_IEEE80211R + os_memcpy(sm->PMK, psk, psk_len); + sm->pmk_len = psk_len; +#ifdef CONFIG_IEEE80211R_AP os_memcpy(sm->xxkey, psk, PMK_LEN); sm->xxkey_len = PMK_LEN; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ + } +#ifdef CONFIG_SAE + if (wpa_auth_uses_sae(sm) && sm->pmksa) { + wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache"); + os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len); + sm->pmk_len = sm->pmksa->pmk_len; } +#endif /* CONFIG_SAE */ sm->req_replay_counter_used = 0; } @@ -2003,7 +2017,7 @@ SM_STATE(WPA_PTK, PTKSTART) sm->alt_snonce_valid = FALSE; sm->TimeoutCtr++; - if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { + if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ return; @@ -2012,11 +2026,23 @@ SM_STATE(WPA_PTK, PTKSTART) 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. + * For infrastructure BSS cases, it is better for the AP not to include + * the PMKID KDE in EAPOL-Key msg 1/4 since it could be used to initiate + * offline search for the passphrase/PSK without having to be able to + * capture a 4-way handshake from a STA that has access to the network. + * + * For IBSS cases, addition of PMKID KDE could be considered even with + * WPA2-PSK cases that use multiple PSKs, but only if there is a single + * possible PSK for this STA. However, this should not be done unless + * there is support for using that information on the supplicant side. + * The concern about exposing PMKID unnecessarily in infrastructure BSS + * cases would also apply here, but at least in the IBSS case, this + * would cover a potential real use case. */ if (sm->wpa == WPA_VERSION_WPA2 && - wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) && + (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) || + (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && sm->pmksa) || + wpa_key_mgmt_sae(sm->wpa_key_mgmt)) && sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) { pmkid = buf; pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; @@ -2024,11 +2050,31 @@ SM_STATE(WPA_PTK, PTKSTART) pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID); if (sm->pmksa) { + wpa_hexdump(MSG_DEBUG, + "RSN: Message 1/4 PMKID from PMKSA entry", + sm->pmksa->pmkid, PMKID_LEN); 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 */ + wpa_printf(MSG_DEBUG, + "RSN: No KCK available to derive PMKID for message 1/4"); pmkid = NULL; +#ifdef CONFIG_SAE + } else if (wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { + if (sm->pmkid_set) { + wpa_hexdump(MSG_DEBUG, + "RSN: Message 1/4 PMKID from SAE", + sm->pmkid, PMKID_LEN); + os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], + sm->pmkid, PMKID_LEN); + } else { + /* No PMKID available */ + wpa_printf(MSG_DEBUG, + "RSN: No SAE PMKID available for message 1/4"); + pmkid = NULL; + } +#endif /* CONFIG_SAE */ } else { /* * Calculate PMKID since no PMKSA cache entry was @@ -2036,7 +2082,10 @@ SM_STATE(WPA_PTK, PTKSTART) */ 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)); + sm->wpa_key_mgmt); + wpa_hexdump(MSG_DEBUG, + "RSN: Message 1/4 PMKID derived from PMK", + &pmkid[2 + RSN_SELECTOR_LEN], PMKID_LEN); } } wpa_send_eapol(sm->wpa_auth, sm, @@ -2049,10 +2098,10 @@ 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 +#ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) return wpa_auth_derive_ptk_ft(sm, pmk, ptk); -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion", sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce, @@ -2060,43 +2109,587 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, } +#ifdef CONFIG_FILS + +int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk, + size_t pmk_len, const u8 *snonce, const u8 *anonce, + const u8 *dhss, size_t dhss_len, + struct wpabuf *g_sta, struct wpabuf *g_ap) +{ + u8 ick[FILS_ICK_MAX_LEN]; + size_t ick_len; + int res; + u8 fils_ft[FILS_FT_MAX_LEN]; + size_t fils_ft_len = 0; + + res = fils_pmk_to_ptk(pmk, pmk_len, sm->addr, sm->wpa_auth->addr, + snonce, anonce, dhss, dhss_len, + &sm->PTK, ick, &ick_len, + sm->wpa_key_mgmt, sm->pairwise, + fils_ft, &fils_ft_len); + if (res < 0) + return res; + sm->PTK_valid = TRUE; + sm->tk_already_set = FALSE; + +#ifdef CONFIG_IEEE80211R_AP + if (fils_ft_len) { + struct wpa_authenticator *wpa_auth = sm->wpa_auth; + struct wpa_auth_config *conf = &wpa_auth->conf; + u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN]; + int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); + size_t pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN; + + if (wpa_derive_pmk_r0(fils_ft, fils_ft_len, + conf->ssid, conf->ssid_len, + conf->mobility_domain, + conf->r0_key_holder, + conf->r0_key_holder_len, + sm->addr, pmk_r0, pmk_r0_name, + use_sha384) < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0", + pmk_r0, pmk_r0_len); + wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name", + pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name); + os_memset(fils_ft, 0, sizeof(fils_ft)); + } +#endif /* CONFIG_IEEE80211R_AP */ + + res = fils_key_auth_sk(ick, ick_len, snonce, anonce, + sm->addr, sm->wpa_auth->addr, + g_sta ? wpabuf_head(g_sta) : NULL, + g_sta ? wpabuf_len(g_sta) : 0, + g_ap ? wpabuf_head(g_ap) : NULL, + g_ap ? wpabuf_len(g_ap) : 0, + sm->wpa_key_mgmt, sm->fils_key_auth_sta, + sm->fils_key_auth_ap, + &sm->fils_key_auth_len); + os_memset(ick, 0, sizeof(ick)); + + /* Store nonces for (Re)Association Request/Response frame processing */ + os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN); + os_memcpy(sm->ANonce, anonce, FILS_NONCE_LEN); + + return res; +} + + +static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk, + u8 *buf, size_t buf_len, u16 *_key_data_len) +{ + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + u8 *pos; + u16 key_data_len; + u8 *tmp; + const u8 *aad[1]; + size_t aad_len[1]; + + hdr = (struct ieee802_1x_hdr *) buf; + key = (struct wpa_eapol_key *) (hdr + 1); + pos = (u8 *) (key + 1); + key_data_len = WPA_GET_BE16(pos); + if (key_data_len < AES_BLOCK_SIZE || + key_data_len > buf_len - sizeof(*hdr) - sizeof(*key) - 2) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "No room for AES-SIV data in the frame"); + return -1; + } + pos += 2; /* Pointing at the Encrypted Key Data field */ + + tmp = os_malloc(key_data_len); + if (!tmp) + return -1; + + /* AES-SIV AAD from EAPOL protocol version field (inclusive) to + * to Key Data (exclusive). */ + aad[0] = buf; + aad_len[0] = pos - buf; + if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, key_data_len, + 1, aad, aad_len, tmp) < 0) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "Invalid AES-SIV data in the frame"); + bin_clear_free(tmp, key_data_len); + return -1; + } + + /* AEAD decryption and validation completed successfully */ + key_data_len -= AES_BLOCK_SIZE; + wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data", + tmp, key_data_len); + + /* Replace Key Data field with the decrypted version */ + os_memcpy(pos, tmp, key_data_len); + pos -= 2; /* Key Data Length field */ + WPA_PUT_BE16(pos, key_data_len); + bin_clear_free(tmp, key_data_len); + if (_key_data_len) + *_key_data_len = key_data_len; + return 0; +} + + +const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm, + const u8 *ies, size_t ies_len, + const u8 *fils_session) +{ + const u8 *ie, *end; + const u8 *session = NULL; + + if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + wpa_printf(MSG_DEBUG, + "FILS: Not a FILS AKM - reject association"); + return NULL; + } + + /* Verify Session element */ + ie = ies; + end = ((const u8 *) ie) + ies_len; + while (ie + 1 < end) { + if (ie + 2 + ie[1] > end) + break; + if (ie[0] == WLAN_EID_EXTENSION && + ie[1] >= 1 + FILS_SESSION_LEN && + ie[2] == WLAN_EID_EXT_FILS_SESSION) { + session = ie; + break; + } + ie += 2 + ie[1]; + } + + if (!session) { + wpa_printf(MSG_DEBUG, + "FILS: %s: Could not find FILS Session element in Assoc Req - reject", + __func__); + return NULL; + } + + if (!fils_session) { + wpa_printf(MSG_DEBUG, + "FILS: %s: Could not find FILS Session element in STA entry - reject", + __func__); + return NULL; + } + + if (os_memcmp(fils_session, session + 3, FILS_SESSION_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FILS: Session mismatch"); + wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session", + fils_session, FILS_SESSION_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session", + session + 3, FILS_SESSION_LEN); + return NULL; + } + return session; +} + + +int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies, + size_t ies_len) +{ + struct ieee802_11_elems elems; + + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "FILS: Failed to parse decrypted elements"); + return -1; + } + + if (!elems.fils_session) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Session element"); + return -1; + } + + if (!elems.fils_key_confirm) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element"); + return -1; + } + + if (elems.fils_key_confirm_len != sm->fils_key_auth_len) { + wpa_printf(MSG_DEBUG, + "FILS: Unexpected Key-Auth length %d (expected %d)", + elems.fils_key_confirm_len, + (int) sm->fils_key_auth_len); + return -1; + } + + if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_sta, + sm->fils_key_auth_len) != 0) { + wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch"); + wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth", + elems.fils_key_confirm, elems.fils_key_confirm_len); + wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth", + sm->fils_key_auth_sta, sm->fils_key_auth_len); + return -1; + } + + return 0; +} + + +int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session, + const struct ieee80211_mgmt *mgmt, size_t frame_len, + u8 *pos, size_t left) +{ + u16 fc, stype; + const u8 *end, *ie_start, *ie, *session, *crypt; + const u8 *aad[5]; + size_t aad_len[5]; + + if (!sm || !sm->PTK_valid) { + wpa_printf(MSG_DEBUG, + "FILS: No KEK to decrypt Assocication Request frame"); + return -1; + } + + if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + wpa_printf(MSG_DEBUG, + "FILS: Not a FILS AKM - reject association"); + return -1; + } + + end = ((const u8 *) mgmt) + frame_len; + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + if (stype == WLAN_FC_STYPE_REASSOC_REQ) + ie_start = mgmt->u.reassoc_req.variable; + else + ie_start = mgmt->u.assoc_req.variable; + ie = ie_start; + + /* + * Find FILS Session element which is the last unencrypted element in + * the frame. + */ + session = wpa_fils_validate_fils_session(sm, ie, end - ie, + fils_session); + if (!session) { + wpa_printf(MSG_DEBUG, "FILS: Session validation failed"); + return -1; + } + + crypt = session + 2 + session[1]; + + if (end - crypt < AES_BLOCK_SIZE) { + wpa_printf(MSG_DEBUG, + "FILS: Too short frame to include AES-SIV data"); + return -1; + } + + /* AES-SIV AAD vectors */ + + /* The STA's MAC address */ + aad[0] = mgmt->sa; + aad_len[0] = ETH_ALEN; + /* The AP's BSSID */ + aad[1] = mgmt->da; + aad_len[1] = ETH_ALEN; + /* The STA's nonce */ + aad[2] = sm->SNonce; + aad_len[2] = FILS_NONCE_LEN; + /* The AP's nonce */ + aad[3] = sm->ANonce; + aad_len[3] = FILS_NONCE_LEN; + /* + * The (Re)Association Request frame from the Capability Information + * field to the FILS Session element (both inclusive). + */ + aad[4] = (const u8 *) &mgmt->u.assoc_req.capab_info; + aad_len[4] = crypt - aad[4]; + + if (aes_siv_decrypt(sm->PTK.kek, sm->PTK.kek_len, crypt, end - crypt, + 5, aad, aad_len, pos + (crypt - ie_start)) < 0) { + wpa_printf(MSG_DEBUG, + "FILS: Invalid AES-SIV data in the frame"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Request elements", + pos, left - AES_BLOCK_SIZE); + + if (wpa_fils_validate_key_confirm(sm, pos, left - AES_BLOCK_SIZE) < 0) { + wpa_printf(MSG_DEBUG, "FILS: Key Confirm validation failed"); + return -1; + } + + return left - AES_BLOCK_SIZE; +} + + +int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf, + size_t current_len, size_t max_len, + const struct wpabuf *hlp) +{ + u8 *end = buf + max_len; + u8 *pos = buf + current_len; + struct ieee80211_mgmt *mgmt; + struct wpabuf *plain; + const u8 *aad[5]; + size_t aad_len[5]; + + if (!sm || !sm->PTK_valid) + return -1; + + wpa_hexdump(MSG_DEBUG, + "FILS: Association Response frame before FILS processing", + buf, current_len); + + mgmt = (struct ieee80211_mgmt *) buf; + + /* AES-SIV AAD vectors */ + + /* The AP's BSSID */ + aad[0] = mgmt->sa; + aad_len[0] = ETH_ALEN; + /* The STA's MAC address */ + aad[1] = mgmt->da; + aad_len[1] = ETH_ALEN; + /* The AP's nonce */ + aad[2] = sm->ANonce; + aad_len[2] = FILS_NONCE_LEN; + /* The STA's nonce */ + aad[3] = sm->SNonce; + aad_len[3] = FILS_NONCE_LEN; + /* + * The (Re)Association Response frame from the Capability Information + * field (the same offset in both Association and Reassociation + * Response frames) to the FILS Session element (both inclusive). + */ + aad[4] = (const u8 *) &mgmt->u.assoc_resp.capab_info; + aad_len[4] = pos - aad[4]; + + /* The following elements will be encrypted with AES-SIV */ + plain = fils_prepare_plainbuf(sm, hlp); + if (!plain) { + wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed"); + return -1; + } + + if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) { + wpa_printf(MSG_DEBUG, + "FILS: Not enough room for FILS elements"); + wpabuf_free(plain); + return -1; + } + + wpa_hexdump_buf_key(MSG_DEBUG, "FILS: Association Response plaintext", + plain); + + if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, + wpabuf_head(plain), wpabuf_len(plain), + 5, aad, aad_len, pos) < 0) { + wpabuf_free(plain); + return -1; + } + + wpa_hexdump(MSG_DEBUG, + "FILS: Encrypted Association Response elements", + pos, AES_BLOCK_SIZE + wpabuf_len(plain)); + current_len += wpabuf_len(plain) + AES_BLOCK_SIZE; + wpabuf_free(plain); + + sm->fils_completed = 1; + + return current_len; +} + + +static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm, + const struct wpabuf *hlp) +{ + struct wpabuf *plain; + u8 *len, *tmp, *tmp2; + u8 hdr[2]; + u8 *gtk, dummy_gtk[32]; + size_t gtk_len; + struct wpa_group *gsm; + + plain = wpabuf_alloc(1000); + if (!plain) + return NULL; + + /* TODO: FILS Public Key */ + + /* FILS Key Confirmation */ + wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(plain, 1 + sm->fils_key_auth_len); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(plain, WLAN_EID_EXT_FILS_KEY_CONFIRM); + wpabuf_put_data(plain, sm->fils_key_auth_ap, sm->fils_key_auth_len); + + /* FILS HLP Container */ + if (hlp) + wpabuf_put_buf(plain, hlp); + + /* TODO: FILS IP Address Assignment */ + + /* Key Delivery */ + gsm = sm->group; + wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */ + len = wpabuf_put(plain, 1); + wpabuf_put_u8(plain, WLAN_EID_EXT_KEY_DELIVERY); + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, + wpabuf_put(plain, WPA_KEY_RSC_LEN)); + /* GTK KDE */ + gtk = gsm->GTK[gsm->GN - 1]; + gtk_len = gsm->GTK_len; + if (sm->wpa_auth->conf.disable_gtk || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { + /* + * Provide unique random GTK to each STA to prevent use + * of GTK in the BSS. + */ + if (random_get_bytes(dummy_gtk, gtk_len) < 0) { + wpabuf_free(plain); + return NULL; + } + gtk = dummy_gtk; + } + hdr[0] = gsm->GN & 0x03; + hdr[1] = 0; + tmp = wpabuf_put(plain, 0); + tmp2 = wpa_add_kde(tmp, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gtk, gtk_len); + wpabuf_put(plain, tmp2 - tmp); + + /* IGTK KDE */ + tmp = wpabuf_put(plain, 0); + tmp2 = ieee80211w_kde_add(sm, tmp); + wpabuf_put(plain, tmp2 - tmp); + + *len = (u8 *) wpabuf_put(plain, 0) - len - 1; + return plain; +} + + +int fils_set_tk(struct wpa_state_machine *sm) +{ + enum wpa_alg alg; + int klen; + + if (!sm || !sm->PTK_valid) { + wpa_printf(MSG_DEBUG, "FILS: No valid PTK available to set TK"); + return -1; + } + if (sm->tk_already_set) { + wpa_printf(MSG_DEBUG, "FILS: TK already set to the driver"); + return -1; + } + + alg = wpa_cipher_to_alg(sm->pairwise); + klen = wpa_cipher_key_len(sm->pairwise); + + wpa_printf(MSG_DEBUG, "FILS: Configure TK to the driver"); + if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, + sm->PTK.tk, klen)) { + wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver"); + return -1; + } + sm->tk_already_set = TRUE; + + return 0; +} + + +u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *buf, + const u8 *fils_session, struct wpabuf *hlp) +{ + struct wpabuf *plain; + u8 *pos = buf; + + /* FILS Session */ + *pos++ = WLAN_EID_EXTENSION; /* Element ID */ + *pos++ = 1 + FILS_SESSION_LEN; /* Length */ + *pos++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */ + os_memcpy(pos, fils_session, FILS_SESSION_LEN); + pos += FILS_SESSION_LEN; + + plain = fils_prepare_plainbuf(sm, hlp); + if (!plain) { + wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed"); + return NULL; + } + + os_memcpy(pos, wpabuf_head(plain), wpabuf_len(plain)); + pos += wpabuf_len(plain); + + wpa_printf(MSG_DEBUG, "%s: plain buf_len: %u", __func__, + (unsigned int) wpabuf_len(plain)); + wpabuf_free(plain); + sm->fils_completed = 1; + return pos; +} + +#endif /* CONFIG_FILS */ + + SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) { + struct wpa_authenticator *wpa_auth = sm->wpa_auth; struct wpa_ptk PTK; int ok = 0, psk_found = 0; const u8 *pmk = NULL; - unsigned int pmk_len; + size_t pmk_len; + int ft; + const u8 *eapol_key_ie, *key_data, *mic; + u16 key_data_length; + size_t mic_len, eapol_key_ie_len; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + struct wpa_eapol_ie_parse kde; SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); sm->EAPOLKeyReceived = FALSE; sm->update_snonce = FALSE; + os_memset(&PTK, 0, sizeof(PTK)); + + mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); /* 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)) { + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && + !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, - sm->p2p_dev_addr, pmk); + sm->p2p_dev_addr, pmk, &pmk_len); if (pmk == NULL) break; psk_found = 1; - pmk_len = PMK_LEN; +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) { + os_memcpy(sm->xxkey, pmk, pmk_len); + sm->xxkey_len = pmk_len; + } +#endif /* CONFIG_IEEE80211R_AP */ } else { pmk = sm->PMK; pmk_len = sm->pmk_len; } - wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK); + if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK) < 0) + break; - if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, + if (mic_len && + wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &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)) +#ifdef CONFIG_FILS + if (!mic_len && + wpa_aead_decrypt(sm, &PTK, sm->last_rx_eapol_key, + sm->last_rx_eapol_key_len, NULL) == 0) { + ok = 1; + break; + } +#endif /* CONFIG_FILS */ + + if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || + wpa_key_mgmt_sae(sm->wpa_key_mgmt)) break; } @@ -2108,7 +2701,79 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) return; } -#ifdef CONFIG_IEEE80211R + /* + * Note: last_rx_eapol_key length fields have already been validated in + * wpa_receive(). + */ + hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key; + key = (struct wpa_eapol_key *) (hdr + 1); + mic = (u8 *) (key + 1); + key_data = mic + mic_len + 2; + key_data_length = WPA_GET_BE16(mic + mic_len); + if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) - + sizeof(*key) - mic_len - 2) + 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, + WLAN_REASON_PREV_AUTH_NOT_VALID); + return; + } +#ifdef CONFIG_IEEE80211R_AP + if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) { + wpa_sta_disconnect(wpa_auth, sm->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + return; + } +#endif /* CONFIG_IEEE80211R_AP */ +#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 */ + +#ifdef CONFIG_IEEE80211R_AP if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { /* * Verify that PMKR1Name from EAPOL-Key message 2/4 matches @@ -2127,7 +2792,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) return; } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); @@ -2186,7 +2851,8 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) 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) { + if (sm->wpa_auth->conf.disable_gtk || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { /* * Provide unique random IGTK to each STA to prevent use of * IGTK in the BSS. @@ -2229,7 +2895,12 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) sm->TimeoutEvt = FALSE; sm->TimeoutCtr++; - if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { + if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && + sm->TimeoutCtr > 1) { + /* Do not allow retransmission of EAPOL-Key msg 3/4 */ + return; + } + if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ return; @@ -2259,7 +2930,8 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) secure = 1; gtk = gsm->GTK[gsm->GN - 1]; gtk_len = gsm->GTK_len; - if (sm->wpa_auth->conf.disable_gtk) { + if (sm->wpa_auth->conf.disable_gtk || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { /* * Provide unique random GTK to each STA to prevent use * of GTK in the BSS. @@ -2297,12 +2969,12 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) kde_len = wpa_ie_len + ieee80211w_kde_len(sm); if (gtk) kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP 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 */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_P2P if (WPA_GET_BE32(sm->ip_addr) > 0) kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4; @@ -2314,7 +2986,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) pos = kde; os_memcpy(pos, wpa_ie, wpa_ie_len); pos += wpa_ie_len; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { int res; size_t elen; @@ -2330,7 +3002,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) pos -= wpa_ie_len; pos += elen; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (gtk) { u8 hdr[2]; hdr[0] = keyidx & 0x03; @@ -2340,7 +3012,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) } pos = ieee80211w_kde_add(sm, pos); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { int res; struct wpa_auth_config *conf; @@ -2352,7 +3024,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) 2 + sm->assoc_resp_ftie[1]); res = 2 + sm->assoc_resp_ftie[1]; } else { - res = wpa_write_ftie(conf, conf->r0_key_holder, + int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); + + res = wpa_write_ftie(conf, use_sha384, + conf->r0_key_holder, conf->r0_key_holder_len, NULL, NULL, pos, kde + kde_len - pos, @@ -2377,10 +3052,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) *pos++ = WLAN_EID_TIMEOUT_INTERVAL; *pos++ = 5; *pos++ = WLAN_TIMEOUT_KEY_LIFETIME; - WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60); + WPA_PUT_LE32(pos, conf->r0_key_lifetime); pos += 4; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_P2P if (WPA_GET_BE32(sm->ip_addr) > 0) { u8 addr[3 * 4]; @@ -2393,7 +3068,9 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) #endif /* CONFIG_P2P */ wpa_send_eapol(sm->wpa_auth, sm, - (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC | + (secure ? WPA_KEY_INFO_SECURE : 0) | + (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? + WPA_KEY_INFO_MIC : 0) | WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_KEY_TYPE, _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); @@ -2410,7 +3087,8 @@ SM_STATE(WPA_PTK, PTKINITDONE) 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); + wpa_sta_disconnect(sm->wpa_auth, sm->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); return; } /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ @@ -2423,7 +3101,9 @@ SM_STATE(WPA_PTK, PTKINITDONE) sm->wpa_auth, sm); } - if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || + sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) { wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_authorized, 1); } @@ -2449,9 +3129,9 @@ SM_STATE(WPA_PTK, PTKINITDONE) "pairwise key handshake completed (%s)", sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr); -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ } @@ -2495,15 +3175,22 @@ SM_STEP(WPA_PTK) 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) + else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE /* FIX: && 802.1X::keyRun */) SM_ENTER(WPA_PTK, INITPSK); + else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) + SM_ENTER(WPA_PTK, INITPMK); break; case WPA_PTK_INITPMK: if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr, - WPA_EAPOL_keyAvailable) > 0) + WPA_EAPOL_keyAvailable) > 0) { + SM_ENTER(WPA_PTK, PTKSTART); +#ifdef CONFIG_DPP + } else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->pmksa) { SM_ENTER(WPA_PTK, PTKSTART); - else { +#endif /* CONFIG_DPP */ + } else { wpa_auth->dot11RSNA4WayHandshakeFailures++; wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, "INITPMK - keyAvailable = false"); @@ -2512,9 +3199,13 @@ SM_STEP(WPA_PTK) break; case WPA_PTK_INITPSK: if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, - NULL)) + NULL, NULL)) { SM_ENTER(WPA_PTK, PTKSTART); - else { +#ifdef CONFIG_SAE + } else if (wpa_auth_uses_sae(sm) && sm->pmksa) { + SM_ENTER(WPA_PTK, PTKSTART); +#endif /* CONFIG_SAE */ + } else { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, "no PSK configured for the STA"); wpa_auth->dot11RSNA4WayHandshakeFailures++; @@ -2526,11 +3217,12 @@ SM_STEP(WPA_PTK) sm->EAPOLKeyPairwise) SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); else if (sm->TimeoutCtr > - (int) dot11RSNAConfigPairwiseUpdateCount) { + sm->wpa_auth->conf.wpa_pairwise_update_count) { wpa_auth->dot11RSNA4WayHandshakeFailures++; - wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "PTKSTART: Retry limit %d reached", - dot11RSNAConfigPairwiseUpdateCount); + wpa_auth_vlogger( + sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PTKSTART: Retry limit %u reached", + sm->wpa_auth->conf.wpa_pairwise_update_count); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKSTART); @@ -2554,12 +3246,14 @@ SM_STEP(WPA_PTK) sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK, PTKINITDONE); else if (sm->TimeoutCtr > - (int) dot11RSNAConfigPairwiseUpdateCount) { + sm->wpa_auth->conf.wpa_pairwise_update_count || + (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && + sm->TimeoutCtr > 1)) { wpa_auth->dot11RSNA4WayHandshakeFailures++; - wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "PTKINITNEGOTIATING: Retry limit %d " - "reached", - dot11RSNAConfigPairwiseUpdateCount); + wpa_auth_vlogger( + sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PTKINITNEGOTIATING: Retry limit %u reached", + sm->wpa_auth->conf.wpa_pairwise_update_count); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); @@ -2594,7 +3288,12 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); sm->GTimeoutCtr++; - if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) { + if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && + sm->GTimeoutCtr > 1) { + /* Do not allow retransmission of EAPOL-Key group msg 1/2 */ + return; + } + if (sm->GTimeoutCtr > sm->wpa_auth->conf.wpa_group_update_count) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ return; @@ -2611,7 +3310,8 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) "sending 1/2 msg of Group Key Handshake"); gtk = gsm->GTK[gsm->GN - 1]; - if (sm->wpa_auth->conf.disable_gtk) { + if (sm->wpa_auth->conf.disable_gtk || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { /* * Provide unique random GTK to each STA to prevent use * of GTK in the BSS. @@ -2640,10 +3340,12 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) } wpa_send_eapol(sm->wpa_auth, sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SECURE | + (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? + WPA_KEY_INFO_MIC : 0) | WPA_KEY_INFO_ACK | (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), - rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1); + rsc, NULL, kde, kde_len, gsm->GN, 1); os_free(kde_buf); } @@ -2672,6 +3374,10 @@ SM_STATE(WPA_PTK_GROUP, KEYERROR) sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; sm->Disconnect = TRUE; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "group key handshake failed (%s) after %u tries", + sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN", + sm->wpa_auth->conf.wpa_group_update_count); } @@ -2691,7 +3397,9 @@ SM_STEP(WPA_PTK_GROUP) !sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED); else if (sm->GTimeoutCtr > - (int) dot11RSNAConfigGroupUpdateCount) + sm->wpa_auth->conf.wpa_group_update_count || + (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && + sm->GTimeoutCtr > 1)) SM_ENTER(WPA_PTK_GROUP, KEYERROR); else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); @@ -2794,7 +3502,7 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) } -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP /* update GTK when exiting WNM-Sleep Mode */ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm) { @@ -2873,7 +3581,7 @@ int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos) return pos - start; } #endif /* CONFIG_IEEE80211W */ -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, @@ -3151,8 +3859,8 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", RSN_VERSION, !!wpa_auth->conf.wpa_strict_rekey, - dot11RSNAConfigGroupUpdateCount, - dot11RSNAConfigPairwiseUpdateCount, + wpa_auth->conf.wpa_group_update_count, + wpa_auth->conf.wpa_pairwise_update_count, wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8, dot11RSNAConfigPMKLifetime, dot11RSNAConfigPMKReauthThreshold, @@ -3279,6 +3987,14 @@ int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm) } +int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm) +{ + if (!sm || !wpa_key_mgmt_fils(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) { @@ -3320,7 +4036,7 @@ int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, sm->wpa_auth->conf.disable_pmksa_caching) return -1; - if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { + if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) { if (pmk_len > PMK_LEN_SUITE_B_192) pmk_len = PMK_LEN_SUITE_B_192; } else if (pmk_len > PMK_LEN) { @@ -3372,6 +4088,29 @@ int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, } +void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid) +{ + os_memcpy(sm->pmkid, pmkid, PMKID_LEN); + sm->pmkid_set = 1; +} + + +int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *pmk, size_t pmk_len, const u8 *pmkid, + int session_timeout, int akmp) +{ + 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, session_timeout, + NULL, akmp)) + return 0; + + return -1; +} + + void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) { @@ -3404,12 +4143,65 @@ void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth) } +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL +#ifdef CONFIG_MESH + +int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr, + char *buf, size_t len) +{ + if (!wpa_auth || !wpa_auth->pmksa) + return 0; + + return pmksa_cache_auth_list_mesh(wpa_auth->pmksa, addr, buf, len); +} + + +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk, + const u8 *pmkid, int expiration) +{ + struct rsn_pmksa_cache_entry *entry; + struct os_reltime now; + + entry = pmksa_cache_auth_create_entry(pmk, PMK_LEN, pmkid, NULL, 0, aa, + spa, 0, NULL, WPA_KEY_MGMT_SAE); + if (!entry) + return NULL; + + os_get_reltime(&now); + entry->expiration = now.sec + expiration; + return entry; +} + + +int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth, + struct rsn_pmksa_cache_entry *entry) +{ + int ret; + + if (!wpa_auth || !wpa_auth->pmksa) + return -1; + + ret = pmksa_cache_auth_add_entry(wpa_auth->pmksa, entry); + if (ret < 0) + wpa_printf(MSG_DEBUG, + "RSN: Failed to store external PMKSA cache for " + MACSTR, MAC2STR(entry->spa)); + + return ret; +} + +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ + + struct rsn_pmksa_cache_entry * -wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) +wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 *pmkid) { if (!wpa_auth || !wpa_auth->pmksa) return NULL; - return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL); + return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, pmkid); } @@ -3662,6 +4454,14 @@ void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, (timeout_ms % 1000) * 1000, wpa_send_eapol_timeout, wpa_auth, sm); } + +#ifdef CONFIG_TESTING_OPTIONS + if (sm->eapol_status_cb) { + sm->eapol_status_cb(sm->eapol_status_cb_ctx1, + sm->eapol_status_cb_ctx2); + sm->eapol_status_cb = NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ } @@ -3708,3 +4508,343 @@ void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth) for (group = wpa_auth->group; group; group = group->next) wpa_group_config_group_keys(wpa_auth, group); } + + +#ifdef CONFIG_FILS + +struct wpa_auth_fils_iter_data { + struct wpa_authenticator *auth; + const u8 *cache_id; + struct rsn_pmksa_cache_entry *pmksa; + const u8 *spa; + const u8 *pmkid; +}; + + +static int wpa_auth_fils_iter(struct wpa_authenticator *a, void *ctx) +{ + struct wpa_auth_fils_iter_data *data = ctx; + + if (a == data->auth || !a->conf.fils_cache_id_set || + os_memcmp(a->conf.fils_cache_id, data->cache_id, + FILS_CACHE_ID_LEN) != 0) + return 0; + data->pmksa = pmksa_cache_auth_get(a->pmksa, data->spa, data->pmkid); + return data->pmksa != NULL; +} + + +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, const u8 *pmkid) +{ + struct wpa_auth_fils_iter_data idata; + + if (!wpa_auth->conf.fils_cache_id_set) + return NULL; + idata.auth = wpa_auth; + idata.cache_id = wpa_auth->conf.fils_cache_id; + idata.pmksa = NULL; + idata.spa = sta_addr; + idata.pmkid = pmkid; + wpa_auth_for_each_auth(wpa_auth, wpa_auth_fils_iter, &idata); + return idata.pmksa; +} + + +#ifdef CONFIG_IEEE80211R_AP +int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384, + u8 *buf, size_t len) +{ + struct wpa_auth_config *conf = &wpa_auth->conf; + + return wpa_write_ftie(conf, use_sha384, conf->r0_key_holder, + conf->r0_key_holder_len, + NULL, NULL, buf, len, NULL, 0); +} +#endif /* CONFIG_IEEE80211R_AP */ + + +void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm, + u8 *fils_anonce, u8 *fils_snonce, + u8 *fils_kek, size_t *fils_kek_len) +{ + os_memcpy(fils_anonce, sm->ANonce, WPA_NONCE_LEN); + os_memcpy(fils_snonce, sm->SNonce, WPA_NONCE_LEN); + os_memcpy(fils_kek, sm->PTK.kek, WPA_KEK_MAX_LEN); + *fils_kek_len = sm->PTK.kek_len; +} + +#endif /* CONFIG_FILS */ + + +#ifdef CONFIG_TESTING_OPTIONS + +int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce, + void (*cb)(void *ctx1, void *ctx2), + void *ctx1, void *ctx2) +{ + const u8 *anonce = sm->ANonce; + u8 anonce_buf[WPA_NONCE_LEN]; + + if (change_anonce) { + if (random_get_bytes(anonce_buf, WPA_NONCE_LEN)) + return -1; + anonce = anonce_buf; + } + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/4 msg of 4-Way Handshake (TESTING)"); + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL, + anonce, NULL, 0, 0, 0); + return 0; +} + + +int wpa_auth_resend_m3(struct wpa_state_machine *sm, + void (*cb)(void *ctx1, void *ctx2), + void *ctx1, void *ctx2) +{ + u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos; +#ifdef CONFIG_IEEE80211W + u8 *opos; +#endif /* CONFIG_IEEE80211W */ + size_t gtk_len, kde_len; + struct wpa_group *gsm = sm->group; + u8 *wpa_ie; + int wpa_ie_len, secure, keyidx, encr = 0; + + /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE], + GTK[GN], IGTK, [FTIE], [TIE * 2]) + */ + + /* Use 0 RSC */ + os_memset(rsc, 0, WPA_KEY_RSC_LEN); + /* 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 (TESTING)"); + 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; + 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_AP + 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_AP */ + kde = os_malloc(kde_len); + if (kde == NULL) + return -1; + + pos = kde; + os_memcpy(pos, wpa_ie, wpa_ie_len); + pos += wpa_ie_len; +#ifdef CONFIG_IEEE80211R_AP + 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 -1; + } + pos -= wpa_ie_len; + pos += elen; + } +#endif /* CONFIG_IEEE80211R_AP */ + 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); + } +#ifdef CONFIG_IEEE80211W + opos = pos; + pos = ieee80211w_kde_add(sm, pos); + if (pos - opos >= 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) { + /* skip KDE header and keyid */ + opos += 2 + RSN_SELECTOR_LEN + 2; + os_memset(opos, 0, 6); /* clear PN */ + } +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_IEEE80211R_AP + 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 { + int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); + + res = wpa_write_ftie(conf, use_sha384, + 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 -1; + } + 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); + pos += 4; + } +#endif /* CONFIG_IEEE80211R_AP */ + + wpa_send_eapol(sm->wpa_auth, sm, + (secure ? WPA_KEY_INFO_SECURE : 0) | + (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? + WPA_KEY_INFO_MIC : 0) | + WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | + WPA_KEY_INFO_KEY_TYPE, + _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); + os_free(kde); + return 0; +} + + +int wpa_auth_resend_group_m1(struct wpa_state_machine *sm, + void (*cb)(void *ctx1, void *ctx2), + void *ctx1, void *ctx2) +{ + u8 rsc[WPA_KEY_RSC_LEN]; + struct wpa_group *gsm = sm->group; + const u8 *kde; + u8 *kde_buf = NULL, *pos, hdr[2]; +#ifdef CONFIG_IEEE80211W + u8 *opos; +#endif /* CONFIG_IEEE80211W */ + size_t kde_len; + u8 *gtk; + + /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */ + os_memset(rsc, 0, WPA_KEY_RSC_LEN); + /* Use 0 RSC */ + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/2 msg of Group Key Handshake (TESTING)"); + + gtk = gsm->GTK[gsm->GN - 1]; + 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 -1; + + 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); +#ifdef CONFIG_IEEE80211W + opos = pos; + pos = ieee80211w_kde_add(sm, pos); + if (pos - opos >= + 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) { + /* skip KDE header and keyid */ + opos += 2 + RSN_SELECTOR_LEN + 2; + os_memset(opos, 0, 6); /* clear PN */ + } +#endif /* CONFIG_IEEE80211W */ + kde_len = pos - kde; + } else { + kde = gtk; + kde_len = gsm->GTK_len; + } + + sm->eapol_status_cb = cb; + sm->eapol_status_cb_ctx1 = ctx1; + sm->eapol_status_cb_ctx2 = ctx2; + + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_SECURE | + (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? + WPA_KEY_INFO_MIC : 0) | + WPA_KEY_INFO_ACK | + (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), + rsc, NULL, kde, kde_len, gsm->GN, 1); + + os_free(kde_buf); + return 0; +} + + +int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth) +{ + if (!wpa_auth) + return -1; + eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); + return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL); +} + +#endif /* CONFIG_TESTING_OPTIONS */ diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 97461b029d24..fad5536f740e 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -1,6 +1,6 @@ /* * hostapd - IEEE 802.11i-2004 / WPA Authenticator - * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,6 +14,8 @@ #include "common/wpa_common.h" #include "common/ieee802_11_defs.h" +struct vlan_description; + #define MAX_OWN_IE_OVERRIDE 256 #ifdef _MSC_VER @@ -37,74 +39,100 @@ struct ft_rrb_frame { #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) +/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r. These + * use OUI Extended EtherType as the encapsulating format. */ +#define FT_PACKET_R0KH_R1KH_PULL 0x01 +#define FT_PACKET_R0KH_R1KH_RESP 0x02 +#define FT_PACKET_R0KH_R1KH_PUSH 0x03 +#define FT_PACKET_R0KH_R1KH_SEQ_REQ 0x04 +#define FT_PACKET_R0KH_R1KH_SEQ_RESP 0x05 -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]; +/* packet layout + * IEEE 802 extended OUI ethertype frame header + * u16 authlen (little endian) + * multiple of struct ft_rrb_tlv (authenticated only, length = authlen) + * multiple of struct ft_rrb_tlv (AES-SIV encrypted, AES-SIV needs an extra + * blocksize length) + * + * AES-SIV AAD; + * source MAC address (6) + * authenticated-only TLVs (authlen) + * subtype (1; FT_PACKET_*) + */ - 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_RRB_NONCE_LEN 16 -#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]; +#define FT_RRB_LAST_EMPTY 0 /* placeholder or padding */ - 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_RRB_SEQ 1 /* struct ft_rrb_seq */ +#define FT_RRB_NONCE 2 /* size FT_RRB_NONCE_LEN */ +#define FT_RRB_TIMESTAMP 3 /* le32 unix seconds */ -#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]; +#define FT_RRB_R0KH_ID 4 /* FT_R0KH_ID_MAX_LEN */ +#define FT_RRB_R1KH_ID 5 /* FT_R1KH_ID_LEN */ +#define FT_RRB_S1KH_ID 6 /* ETH_ALEN */ + +#define FT_RRB_PMK_R0_NAME 7 /* WPA_PMK_NAME_LEN */ +#define FT_RRB_PMK_R0 8 /* PMK_LEN */ +#define FT_RRB_PMK_R1_NAME 9 /* WPA_PMK_NAME_LEN */ +#define FT_RRB_PMK_R1 10 /* PMK_LEN */ - /* 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]; +#define FT_RRB_PAIRWISE 11 /* le16 */ +#define FT_RRB_EXPIRES_IN 12 /* le16 seconds */ + +#define FT_RRB_VLAN_UNTAGGED 13 /* le16 */ +#define FT_RRB_VLAN_TAGGED 14 /* n times le16 */ + +#define FT_RRB_IDENTITY 15 +#define FT_RRB_RADIUS_CUI 16 +#define FT_RRB_SESSION_TIMEOUT 17 /* le32 seconds */ + +struct ft_rrb_tlv { + le16 type; + le16 len; + /* followed by data of length len */ } STRUCT_PACKED; +struct ft_rrb_seq { + le32 dom; + le32 seq; + le32 ts; +} STRUCT_PACKED; + +/* session TLVs: + * required: PMK_R1, PMK_R1_NAME, PAIRWISE + * optional: VLAN_UNTAGGED, VLAN_TAGGED, EXPIRES_IN, IDENTITY, RADIUS_CUI, + * SESSION_TIMEOUT + * + * pull frame TLVs: + * auth: + * required: SEQ, NONCE, R0KH_ID, R1KH_ID + * encrypted: + * required: PMK_R0_NAME, S1KH_ID + * + * response frame TLVs: + * auth: + * required: SEQ, NONCE, R0KH_ID, R1KH_ID + * encrypted: + * required: S1KH_ID + * optional: session TLVs + * + * push frame TLVs: + * auth: + * required: SEQ, R0KH_ID, R1KH_ID + * encrypted: + * required: S1KH_ID, PMK_R0_NAME, session TLVs + * + * sequence number request frame TLVs: + * auth: + * required: R0KH_ID, R1KH_ID, NONCE + * + * sequence number response frame TLVs: + * auth: + * required: SEQ, NONCE, R0KH_ID, R1KH_ID + */ + #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ @@ -116,6 +144,7 @@ struct wpa_authenticator; struct wpa_state_machine; struct rsn_pmksa_cache_entry; struct eapol_state_machine; +struct ft_remote_seq; struct ft_remote_r0kh { @@ -123,7 +152,8 @@ struct ft_remote_r0kh { u8 addr[ETH_ALEN]; u8 id[FT_R0KH_ID_MAX_LEN]; size_t id_len; - u8 key[16]; + u8 key[32]; + struct ft_remote_seq *seq; }; @@ -131,7 +161,8 @@ struct ft_remote_r1kh { struct ft_remote_r1kh *next; u8 addr[ETH_ALEN]; u8 id[FT_R1KH_ID_LEN]; - u8 key[16]; + u8 key[32]; + struct ft_remote_seq *seq; }; @@ -144,10 +175,12 @@ struct wpa_auth_config { int wpa_strict_rekey; int wpa_gmk_rekey; int wpa_ptk_rekey; + u32 wpa_group_update_count; + u32 wpa_pairwise_update_count; + int wpa_disable_eapol_key_retries; int rsn_pairwise; int rsn_preauth; int eapol_version; - int peerkey; int wmm_enabled; int wmm_uapsd; int disable_pmksa_caching; @@ -156,21 +189,28 @@ struct wpa_auth_config { #ifdef CONFIG_IEEE80211W enum mfp_options ieee80211w; int group_mgmt_cipher; + int sae_require_mfp; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP 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 r0_key_lifetime; /* PMK-R0 lifetime seconds */ + int rkh_pos_timeout; + int rkh_neg_timeout; + int rkh_pull_timeout; /* ms */ + int rkh_pull_retries; + int r1_max_key_lifetime; u32 reassociation_deadline; - struct ft_remote_r0kh *r0kh_list; - struct ft_remote_r1kh *r1kh_list; + struct ft_remote_r0kh **r0kh_list; + struct ft_remote_r1kh **r1kh_list; int pmk_r1_push; int ft_over_ds; -#endif /* CONFIG_IEEE80211R */ + int ft_psk_generate_local; +#endif /* CONFIG_IEEE80211R_AP */ int disable_gtk; int ap_mlme; #ifdef CONFIG_TESTING_OPTIONS @@ -184,6 +224,10 @@ struct wpa_auth_config { u8 ip_addr_start[4]; u8 ip_addr_end[4]; #endif /* CONFIG_P2P */ +#ifdef CONFIG_FILS + unsigned int fils_cache_id_set:1; + u8 fils_cache_id[FILS_CACHE_ID_LEN]; +#endif /* CONFIG_FILS */ }; typedef enum { @@ -197,7 +241,6 @@ typedef enum { } 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); @@ -207,7 +250,7 @@ struct wpa_auth_callbacks { 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); + const u8 *prev_psk, size_t *psk_len); 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); @@ -220,13 +263,29 @@ struct wpa_auth_callbacks { 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 + int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data, + size_t data_len); +#ifdef CONFIG_IEEE80211R_AP struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); + int (*set_vlan)(void *ctx, const u8 *sta_addr, + struct vlan_description *vlan); + int (*get_vlan)(void *ctx, const u8 *sta_addr, + struct vlan_description *vlan); + int (*set_identity)(void *ctx, const u8 *sta_addr, + const u8 *identity, size_t identity_len); + size_t (*get_identity)(void *ctx, const u8 *sta_addr, const u8 **buf); + int (*set_radius_cui)(void *ctx, const u8 *sta_addr, + const u8 *radius_cui, size_t radius_cui_len); + size_t (*get_radius_cui)(void *ctx, const u8 *sta_addr, const u8 **buf); + void (*set_session_timeout)(void *ctx, const u8 *sta_addr, + int session_timeout); + int (*get_session_timeout)(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 */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_MESH int (*start_ampe)(void *ctx, const u8 *sta_addr); #endif /* CONFIG_MESH */ @@ -234,7 +293,8 @@ struct wpa_auth_callbacks { struct wpa_authenticator * wpa_init(const u8 *addr, struct wpa_auth_config *conf, - struct wpa_auth_callbacks *cb); + const struct wpa_auth_callbacks *cb, + void *cb_ctx); 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, @@ -244,13 +304,14 @@ 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 + WPA_INVALID_MDIE, WPA_INVALID_PROTO, WPA_INVALID_PMKID }; - + 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); + const u8 *mdie, size_t mdie_len, + const u8 *owe_dh, size_t owe_dh_len); int wpa_validate_osen(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, const u8 *osen_ie, size_t osen_ie_len); @@ -267,7 +328,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, 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_DRV_STA_REMOVED + WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_ASSOC_FILS, 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); @@ -281,6 +342,7 @@ 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_fils_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 * @@ -297,13 +359,28 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, 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_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid); +int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *pmk, size_t pmk_len, const u8 *pmkid, + int session_timeout, int akmp); 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); +int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr, + char *buf, size_t len); +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk, + const u8 *pmkid, int expiration); +int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth, + struct rsn_pmksa_cache_entry *entry); struct rsn_pmksa_cache_entry * -wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr); +wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 *pmkid); +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, const u8 *pmkid); void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa, struct wpa_state_machine *sm, struct wpa_authenticator *wpa_auth, @@ -312,7 +389,7 @@ 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 +#ifdef CONFIG_IEEE80211R_AP 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); @@ -327,8 +404,13 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, 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_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, 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_ft_deinit(struct wpa_authenticator *wpa_auth); +void wpa_ft_sta_deinit(struct wpa_state_machine *sm); +#endif /* CONFIG_IEEE80211R_AP */ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm); void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag); @@ -347,5 +429,44 @@ 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); +int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk, + size_t pmk_len, const u8 *snonce, const u8 *anonce, + const u8 *dhss, size_t dhss_len, + struct wpabuf *g_sta, struct wpabuf *g_ap); +int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session, + const struct ieee80211_mgmt *mgmt, size_t frame_len, + u8 *pos, size_t left); +int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf, + size_t current_len, size_t max_len, + const struct wpabuf *hlp); +int fils_set_tk(struct wpa_state_machine *sm); +u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *eid, + const u8 *fils_session, + struct wpabuf *fils_hlp_resp); +const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm, + const u8 *ies, size_t ies_len, + const u8 *fils_session); +int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies, + size_t ies_len); + +int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384, + u8 *buf, size_t len); +void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm, + u8 *fils_anonce, u8 *fils_snonce, + u8 *fils_kek, size_t *fils_kek_len); +u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, + u8 *pos, size_t max_len, + const u8 *req_ies, size_t req_ies_len); + +int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce, + void (*cb)(void *ctx1, void *ctx2), + void *ctx1, void *ctx2); +int wpa_auth_resend_m3(struct wpa_state_machine *sm, + void (*cb)(void *ctx1, void *ctx2), + void *ctx1, void *ctx2); +int wpa_auth_resend_group_m1(struct wpa_state_machine *sm, + void (*cb)(void *ctx1, void *ctx2), + void *ctx1, void *ctx2); +int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth); #endif /* WPA_AUTH_H */ diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index e63b99ad2034..f6792e00f32d 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -1,6 +1,6 @@ /* * hostapd - IEEE 802.11r - Fast BSS Transition - * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -13,7 +13,10 @@ #include "utils/list.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "crypto/aes.h" +#include "crypto/aes_siv.h" #include "crypto/aes_wrap.h" +#include "crypto/sha384.h" #include "crypto/random.h" #include "ap_config.h" #include "ieee802_11.h" @@ -22,41 +25,692 @@ #include "wpa_auth_i.h" -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP + +const unsigned int ftRRBseqTimeout = 10; +const unsigned int ftRRBmaxQueueLen = 100; + static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm, const u8 *current_ap, const u8 *sta_addr, u16 status, const u8 *resp_ies, size_t resp_ies_len); +static void ft_finish_pull(struct wpa_state_machine *sm); +static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx); +static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx); + +struct tlv_list { + u16 type; + size_t len; + const u8 *data; +}; + + +/** + * wpa_ft_rrb_decrypt - Decrypt FT RRB message + * @key: AES-SIV key for AEAD + * @key_len: Length of key in octets + * @enc: Pointer to encrypted TLVs + * @enc_len: Length of encrypted TLVs in octets + * @auth: Pointer to authenticated TLVs + * @auth_len: Length of authenticated TLVs in octets + * @src_addr: MAC address of the frame sender + * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*) + * @plain: Pointer to return the pointer to the allocated plaintext buffer; + * needs to be freed by the caller if not NULL; + * will only be returned on success + * @plain_len: Pointer to return the length of the allocated plaintext buffer + * in octets + * Returns: 0 on success, -1 on error + */ +static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len, + const u8 *enc, const size_t enc_len, + const u8 *auth, const size_t auth_len, + const u8 *src_addr, u8 type, + u8 **plain, size_t *plain_size) +{ + const u8 *ad[3] = { src_addr, auth, &type }; + size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) }; + + wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypt using key", key, key_len); + + if (!key) { /* skip decryption */ + *plain = os_memdup(enc, enc_len); + if (enc_len > 0 && !*plain) + goto err; + + *plain_size = enc_len; + + return 0; + } + + *plain = NULL; + + /* SIV overhead */ + if (enc_len < AES_BLOCK_SIZE) + goto err; + *plain = os_zalloc(enc_len - AES_BLOCK_SIZE); + if (!*plain) + goto err; + + if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len, + *plain) < 0) + goto err; + + *plain_size = enc_len - AES_BLOCK_SIZE; + wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypted TLVs", + *plain, *plain_size); + return 0; +err: + os_free(*plain); + *plain = NULL; + *plain_size = 0; + + wpa_printf(MSG_ERROR, "FT(RRB): Failed to decrypt"); + + return -1; +} + + +/* get first tlv record in packet matching type + * @data (decrypted) packet + * @return 0 on success else -1 + */ +static int wpa_ft_rrb_get_tlv(const u8 *plain, size_t plain_len, + u16 type, size_t *tlv_len, const u8 **tlv_data) +{ + const struct ft_rrb_tlv *f; + size_t left; + le16 type16; + size_t len; + + left = plain_len; + type16 = host_to_le16(type); + + while (left >= sizeof(*f)) { + f = (const struct ft_rrb_tlv *) plain; + + left -= sizeof(*f); + plain += sizeof(*f); + len = le_to_host16(f->len); + + if (left < len) { + wpa_printf(MSG_DEBUG, "FT: RRB message truncated"); + break; + } + + if (f->type == type16) { + *tlv_len = len; + *tlv_data = plain; + return 0; + } + + left -= len; + plain += len; + } + + return -1; +} + + +static void wpa_ft_rrb_dump(const u8 *plain, const size_t plain_len) +{ + const struct ft_rrb_tlv *f; + size_t left; + size_t len; + + left = plain_len; + + wpa_printf(MSG_DEBUG, "FT: RRB dump message"); + while (left >= sizeof(*f)) { + f = (const struct ft_rrb_tlv *) plain; + + left -= sizeof(*f); + plain += sizeof(*f); + len = le_to_host16(f->len); + + wpa_printf(MSG_DEBUG, "FT: RRB TLV type = %d, len = %zu", + le_to_host16(f->type), len); + + if (left < len) { + wpa_printf(MSG_DEBUG, + "FT: RRB message truncated: left %zu bytes, need %zu", + left, len); + break; + } + + wpa_hexdump(MSG_DEBUG, "FT: RRB TLV data", plain, len); + + left -= len; + plain += len; + } + + if (left > 0) + wpa_hexdump(MSG_DEBUG, "FT: RRB TLV padding", plain, left); + + wpa_printf(MSG_DEBUG, "FT: RRB dump message end"); +} + + +static int cmp_int(const void *a, const void *b) +{ + int x, y; + + x = *((int *) a); + y = *((int *) b); + return x - y; +} + + +static int wpa_ft_rrb_get_tlv_vlan(const u8 *plain, const size_t plain_len, + struct vlan_description *vlan) +{ + struct ft_rrb_tlv *f; + size_t left; + size_t len; + int taggedidx; + int vlan_id; + int type; + + left = plain_len; + taggedidx = 0; + os_memset(vlan, 0, sizeof(*vlan)); + + while (left >= sizeof(*f)) { + f = (struct ft_rrb_tlv *) plain; + + left -= sizeof(*f); + plain += sizeof(*f); + + len = le_to_host16(f->len); + type = le_to_host16(f->type); + + if (left < len) { + wpa_printf(MSG_DEBUG, "FT: RRB message truncated"); + return -1; + } + + if (type != FT_RRB_VLAN_UNTAGGED && type != FT_RRB_VLAN_TAGGED) + goto skip; + + if (type == FT_RRB_VLAN_UNTAGGED && len != sizeof(le16)) { + wpa_printf(MSG_DEBUG, + "FT: RRB VLAN_UNTAGGED invalid length"); + return -1; + } + + if (type == FT_RRB_VLAN_TAGGED && len % sizeof(le16) != 0) { + wpa_printf(MSG_DEBUG, + "FT: RRB VLAN_TAGGED invalid length"); + return -1; + } + + while (len >= sizeof(le16)) { + vlan_id = WPA_GET_LE16(plain); + plain += sizeof(le16); + left -= sizeof(le16); + len -= sizeof(le16); + + if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) { + wpa_printf(MSG_DEBUG, + "FT: RRB VLAN ID invalid %d", + vlan_id); + continue; + } + + if (type == FT_RRB_VLAN_UNTAGGED) + vlan->untagged = vlan_id; + + if (type == FT_RRB_VLAN_TAGGED && + taggedidx < MAX_NUM_TAGGED_VLAN) { + vlan->tagged[taggedidx] = vlan_id; + taggedidx++; + } else if (type == FT_RRB_VLAN_TAGGED) { + wpa_printf(MSG_DEBUG, "FT: RRB too many VLANs"); + } + } + + skip: + left -= len; + plain += len; + } + + if (taggedidx) + qsort(vlan->tagged, taggedidx, sizeof(int), cmp_int); + + vlan->notempty = vlan->untagged || vlan->tagged[0]; + + return 0; +} + + +static size_t wpa_ft_tlv_len(const struct tlv_list *tlvs) +{ + size_t tlv_len = 0; + int i; + + if (!tlvs) + return 0; + + for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) { + tlv_len += sizeof(struct ft_rrb_tlv); + tlv_len += tlvs[i].len; + } + + return tlv_len; +} + + +static size_t wpa_ft_tlv_lin(const struct tlv_list *tlvs, u8 *start, + u8 *endpos) +{ + int i; + size_t tlv_len; + struct ft_rrb_tlv *hdr; + u8 *pos; + + if (!tlvs) + return 0; + + tlv_len = 0; + pos = start; + for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) { + if (tlv_len + sizeof(*hdr) > (size_t) (endpos - start)) + return tlv_len; + tlv_len += sizeof(*hdr); + hdr = (struct ft_rrb_tlv *) pos; + hdr->type = host_to_le16(tlvs[i].type); + hdr->len = host_to_le16(tlvs[i].len); + pos = start + tlv_len; + + if (tlv_len + tlvs[i].len > (size_t) (endpos - start)) + return tlv_len; + if (tlvs[i].len == 0) + continue; + tlv_len += tlvs[i].len; + os_memcpy(pos, tlvs[i].data, tlvs[i].len); + pos = start + tlv_len; + } + + return tlv_len; +} + + +static size_t wpa_ft_vlan_len(const struct vlan_description *vlan) +{ + size_t tlv_len = 0; + int i; + + if (!vlan || !vlan->notempty) + return 0; + + if (vlan->untagged) { + tlv_len += sizeof(struct ft_rrb_tlv); + tlv_len += sizeof(le16); + } + if (vlan->tagged[0]) + tlv_len += sizeof(struct ft_rrb_tlv); + for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++) + tlv_len += sizeof(le16); + + return tlv_len; +} + + +static size_t wpa_ft_vlan_lin(const struct vlan_description *vlan, + u8 *start, u8 *endpos) +{ + size_t tlv_len; + int i, len; + struct ft_rrb_tlv *hdr; + u8 *pos = start; + + if (!vlan || !vlan->notempty) + return 0; + + tlv_len = 0; + if (vlan->untagged) { + tlv_len += sizeof(*hdr); + if (start + tlv_len > endpos) + return tlv_len; + hdr = (struct ft_rrb_tlv *) pos; + hdr->type = host_to_le16(FT_RRB_VLAN_UNTAGGED); + hdr->len = host_to_le16(sizeof(le16)); + pos = start + tlv_len; + + tlv_len += sizeof(le16); + if (start + tlv_len > endpos) + return tlv_len; + WPA_PUT_LE16(pos, vlan->untagged); + pos = start + tlv_len; + } + + if (!vlan->tagged[0]) + return tlv_len; + + tlv_len += sizeof(*hdr); + if (start + tlv_len > endpos) + return tlv_len; + hdr = (struct ft_rrb_tlv *) pos; + hdr->type = host_to_le16(FT_RRB_VLAN_TAGGED); + len = 0; /* len is computed below */ + pos = start + tlv_len; + + for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++) { + tlv_len += sizeof(le16); + if (start + tlv_len > endpos) + break; + len += sizeof(le16); + WPA_PUT_LE16(pos, vlan->tagged[i]); + pos = start + tlv_len; + } + + hdr->len = host_to_le16(len); + + return tlv_len; +} + + +static int wpa_ft_rrb_lin(const struct tlv_list *tlvs1, + const struct tlv_list *tlvs2, + const struct vlan_description *vlan, + u8 **plain, size_t *plain_len) +{ + u8 *pos, *endpos; + size_t tlv_len; + + tlv_len = wpa_ft_tlv_len(tlvs1); + tlv_len += wpa_ft_tlv_len(tlvs2); + tlv_len += wpa_ft_vlan_len(vlan); + + *plain_len = tlv_len; + *plain = os_zalloc(tlv_len); + if (!*plain) { + wpa_printf(MSG_ERROR, "FT: Failed to allocate plaintext"); + goto err; + } + + pos = *plain; + endpos = *plain + tlv_len; + pos += wpa_ft_tlv_lin(tlvs1, pos, endpos); + pos += wpa_ft_tlv_lin(tlvs2, pos, endpos); + pos += wpa_ft_vlan_lin(vlan, pos, endpos); + + /* sanity check */ + if (pos != endpos) { + wpa_printf(MSG_ERROR, "FT: Length error building RRB"); + goto err; + } + + return 0; + +err: + os_free(*plain); + *plain = NULL; + *plain_len = 0; + return -1; +} + + +static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len, + const u8 *plain, const size_t plain_len, + const u8 *auth, const size_t auth_len, + const u8 *src_addr, u8 type, u8 *enc) +{ + const u8 *ad[3] = { src_addr, auth, &type }; + size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) }; + + wpa_hexdump_key(MSG_DEBUG, "FT(RRB): plaintext message", + plain, plain_len); + wpa_hexdump_key(MSG_DEBUG, "FT(RRB): encrypt using key", key, key_len); + + if (!key) { + /* encryption not needed, return plaintext as packet */ + os_memcpy(enc, plain, plain_len); + } else if (aes_siv_encrypt(key, key_len, plain, plain_len, + 3, ad, ad_len, enc) < 0) { + wpa_printf(MSG_ERROR, "FT: Failed to encrypt RRB-OUI message"); + return -1; + } + + return 0; +} + + +/** + * wpa_ft_rrb_build - Build and encrypt an FT RRB message + * @key: AES-SIV key for AEAD + * @key_len: Length of key in octets + * @tlvs_enc0: First set of to-be-encrypted TLVs + * @tlvs_enc1: Second set of to-be-encrypted TLVs + * @tlvs_auth: Set of to-be-authenticated TLVs + * @src_addr: MAC address of the frame sender + * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*) + * @packet Pointer to return the pointer to the allocated packet buffer; + * needs to be freed by the caller if not null; + * will only be returned on success + * @packet_len: Pointer to return the length of the allocated buffer in octets + * Returns: 0 on success, -1 on error + */ +static int wpa_ft_rrb_build(const u8 *key, const size_t key_len, + const struct tlv_list *tlvs_enc0, + const struct tlv_list *tlvs_enc1, + const struct tlv_list *tlvs_auth, + const struct vlan_description *vlan, + const u8 *src_addr, u8 type, + u8 **packet, size_t *packet_len) +{ + u8 *plain = NULL, *auth = NULL, *pos; + size_t plain_len = 0, auth_len = 0; + int ret = -1; + + *packet = NULL; + if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, vlan, &plain, &plain_len) < 0) + goto out; + + if (wpa_ft_rrb_lin(tlvs_auth, NULL, NULL, &auth, &auth_len) < 0) + goto out; + + *packet_len = sizeof(u16) + auth_len + plain_len; + if (key) + *packet_len += AES_BLOCK_SIZE; + *packet = os_zalloc(*packet_len); + if (!*packet) + goto out; + + pos = *packet; + WPA_PUT_LE16(pos, auth_len); + pos += 2; + os_memcpy(pos, auth, auth_len); + pos += auth_len; + if (wpa_ft_rrb_encrypt(key, key_len, plain, plain_len, auth, + auth_len, src_addr, type, pos) < 0) + goto out; + + ret = 0; + +out: + bin_clear_free(plain, plain_len); + os_free(auth); + + if (ret) { + wpa_printf(MSG_ERROR, "FT: Failed to build RRB-OUI message"); + os_free(*packet); + *packet = NULL; + *packet_len = 0; + } + + return ret; +} + + +#define RRB_GET_SRC(srcfield, type, field, txt, checklength) do { \ + if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \ + &f_##field##_len, &f_##field) < 0 || \ + (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \ + wpa_printf(MSG_INFO, "FT: Missing required " #field \ + " in %s from " MACSTR, txt, MAC2STR(src_addr)); \ + wpa_ft_rrb_dump(srcfield, srcfield##_len); \ + goto out; \ + } \ +} while (0) + +#define RRB_GET(type, field, txt, checklength) \ + RRB_GET_SRC(plain, type, field, txt, checklength) +#define RRB_GET_AUTH(type, field, txt, checklength) \ + RRB_GET_SRC(auth, type, field, txt, checklength) + +#define RRB_GET_OPTIONAL_SRC(srcfield, type, field, txt, checklength) do { \ + if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \ + &f_##field##_len, &f_##field) < 0 || \ + (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \ + wpa_printf(MSG_DEBUG, "FT: Missing optional " #field \ + " in %s from " MACSTR, txt, MAC2STR(src_addr)); \ + f_##field##_len = 0; \ + f_##field = NULL; \ + } \ +} while (0) + +#define RRB_GET_OPTIONAL(type, field, txt, checklength) \ + RRB_GET_OPTIONAL_SRC(plain, type, field, txt, checklength) +#define RRB_GET_OPTIONAL_AUTH(type, field, txt, checklength) \ + RRB_GET_OPTIONAL_SRC(auth, type, field, txt, checklength) static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst, const u8 *data, size_t data_len) { - if (wpa_auth->cb.send_ether == NULL) + if (wpa_auth->cb->send_ether == NULL) return -1; wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst)); - return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB, - data, data_len); + return wpa_auth->cb->send_ether(wpa_auth->cb_ctx, dst, ETH_P_RRB, + data, data_len); +} + + +static int wpa_ft_rrb_oui_send(struct wpa_authenticator *wpa_auth, + const u8 *dst, u8 oui_suffix, + const u8 *data, size_t data_len) +{ + if (!wpa_auth->cb->send_oui) + return -1; + wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR, + oui_suffix, MAC2STR(dst)); + return wpa_auth->cb->send_oui(wpa_auth->cb_ctx, dst, oui_suffix, data, + data_len); } static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth, const u8 *dst, const u8 *data, size_t data_len) { - if (wpa_auth->cb.send_ft_action == NULL) + if (wpa_auth->cb->send_ft_action == NULL) return -1; - return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst, - data, data_len); + return wpa_auth->cb->send_ft_action(wpa_auth->cb_ctx, dst, + data, data_len); +} + + +static const u8 * wpa_ft_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, NULL); } static struct wpa_state_machine * wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) { - if (wpa_auth->cb.add_sta == NULL) + if (wpa_auth->cb->add_sta == NULL) return NULL; - return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr); + return wpa_auth->cb->add_sta(wpa_auth->cb_ctx, sta_addr); +} + + +static int wpa_ft_set_vlan(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, struct vlan_description *vlan) +{ + if (!wpa_auth->cb->set_vlan) + return -1; + return wpa_auth->cb->set_vlan(wpa_auth->cb_ctx, sta_addr, vlan); +} + + +static int wpa_ft_get_vlan(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, struct vlan_description *vlan) +{ + if (!wpa_auth->cb->get_vlan) + return -1; + return wpa_auth->cb->get_vlan(wpa_auth->cb_ctx, sta_addr, vlan); +} + + +static int +wpa_ft_set_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 *identity, size_t identity_len) +{ + if (!wpa_auth->cb->set_identity) + return -1; + return wpa_auth->cb->set_identity(wpa_auth->cb_ctx, sta_addr, identity, + identity_len); +} + + +static size_t +wpa_ft_get_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 **buf) +{ + *buf = NULL; + if (!wpa_auth->cb->get_identity) + return 0; + return wpa_auth->cb->get_identity(wpa_auth->cb_ctx, sta_addr, buf); +} + + +static int +wpa_ft_set_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 *radius_cui, size_t radius_cui_len) +{ + if (!wpa_auth->cb->set_radius_cui) + return -1; + return wpa_auth->cb->set_radius_cui(wpa_auth->cb_ctx, sta_addr, + radius_cui, radius_cui_len); +} + + +static size_t +wpa_ft_get_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 **buf) +{ + *buf = NULL; + if (!wpa_auth->cb->get_radius_cui) + return 0; + return wpa_auth->cb->get_radius_cui(wpa_auth->cb_ctx, sta_addr, buf); +} + + +static void +wpa_ft_set_session_timeout(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, int session_timeout) +{ + if (!wpa_auth->cb->set_session_timeout) + return; + wpa_auth->cb->set_session_timeout(wpa_auth->cb_ctx, sta_addr, + session_timeout); +} + + +static int +wpa_ft_get_session_timeout(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr) +{ + if (!wpa_auth->cb->get_session_timeout) + return 0; + return wpa_auth->cb->get_session_timeout(wpa_auth->cb_ctx, sta_addr); } @@ -64,12 +718,12 @@ static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, u8 *tspec_ie, size_t tspec_ielen) { - if (wpa_auth->cb.add_tspec == NULL) { + if (wpa_auth->cb->add_tspec == NULL) { wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized"); return -1; } - return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie, - tspec_ielen); + return wpa_auth->cb->add_tspec(wpa_auth->cb_ctx, sta_addr, tspec_ie, + tspec_ielen); } @@ -93,30 +747,44 @@ int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) } -int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, - size_t r0kh_id_len, +int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384, + const u8 *r0kh_id, size_t r0kh_id_len, const u8 *anonce, const u8 *snonce, u8 *buf, size_t len, const u8 *subelem, size_t subelem_len) { u8 *pos = buf, *ielen; - struct rsn_ftie *hdr; + size_t hdrlen = use_sha384 ? sizeof(struct rsn_ftie_sha384) : + sizeof(struct rsn_ftie); - if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len + + if (len < 2 + hdrlen + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len + subelem_len) return -1; *pos++ = WLAN_EID_FAST_BSS_TRANSITION; ielen = pos++; - hdr = (struct rsn_ftie *) pos; - os_memset(hdr, 0, sizeof(*hdr)); - pos += sizeof(*hdr); - WPA_PUT_LE16(hdr->mic_control, 0); - if (anonce) - os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN); - if (snonce) - os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN); + if (use_sha384) { + struct rsn_ftie_sha384 *hdr = (struct rsn_ftie_sha384 *) pos; + + os_memset(hdr, 0, sizeof(*hdr)); + pos += sizeof(*hdr); + WPA_PUT_LE16(hdr->mic_control, 0); + if (anonce) + os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN); + if (snonce) + os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN); + } else { + struct rsn_ftie *hdr = (struct rsn_ftie *) pos; + + os_memset(hdr, 0, sizeof(*hdr)); + pos += sizeof(*hdr); + WPA_PUT_LE16(hdr->mic_control, 0); + if (anonce) + os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN); + if (snonce) + os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN); + } /* Optional Parameters */ *pos++ = FTIE_SUBELEM_R1KH_ID; @@ -142,35 +810,432 @@ int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, } +/* A packet to be handled after seq response */ +struct ft_remote_item { + struct dl_list list; + + u8 nonce[FT_RRB_NONCE_LEN]; + struct os_reltime nonce_ts; + + u8 src_addr[ETH_ALEN]; + u8 *enc; + size_t enc_len; + u8 *auth; + size_t auth_len; + int (*cb)(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer); +}; + + +static void wpa_ft_rrb_seq_free(struct ft_remote_item *item) +{ + eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, ELOOP_ALL_CTX, item); + dl_list_del(&item->list); + bin_clear_free(item->enc, item->enc_len); + os_free(item->auth); + os_free(item); +} + + +static void wpa_ft_rrb_seq_flush(struct wpa_authenticator *wpa_auth, + struct ft_remote_seq *rkh_seq, int cb) +{ + struct ft_remote_item *item, *n; + + dl_list_for_each_safe(item, n, &rkh_seq->rx.queue, + struct ft_remote_item, list) { + if (cb && item->cb) + item->cb(wpa_auth, item->src_addr, item->enc, + item->enc_len, item->auth, item->auth_len, 1); + wpa_ft_rrb_seq_free(item); + } +} + + +static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct ft_remote_item *item = timeout_ctx; + + wpa_ft_rrb_seq_free(item); +} + + +static int +wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth, + struct ft_remote_seq *rkh_seq, const u8 *src_addr, + const u8 *f_r0kh_id, size_t f_r0kh_id_len, + const u8 *f_r1kh_id, const u8 *key, size_t key_len, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int (*cb)(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer)) +{ + struct ft_remote_item *item = NULL; + u8 *packet = NULL; + size_t packet_len; + struct tlv_list seq_req_auth[] = { + { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN, + .data = NULL /* to be filled: item->nonce */ }, + { .type = FT_RRB_R0KH_ID, .len = f_r0kh_id_len, + .data = f_r0kh_id }, + { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN, + .data = f_r1kh_id }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + + if (dl_list_len(&rkh_seq->rx.queue) >= ftRRBmaxQueueLen) { + wpa_printf(MSG_DEBUG, "FT: Sequence number queue too long"); + goto err; + } + + item = os_zalloc(sizeof(*item)); + if (!item) + goto err; + + os_memcpy(item->src_addr, src_addr, ETH_ALEN); + item->cb = cb; + + if (random_get_bytes(item->nonce, FT_RRB_NONCE_LEN) < 0) { + wpa_printf(MSG_DEBUG, "FT: Seq num nonce: out of random bytes"); + goto err; + } + + if (os_get_reltime(&item->nonce_ts) < 0) + goto err; + + if (enc && enc_len > 0) { + item->enc = os_memdup(enc, enc_len); + item->enc_len = enc_len; + if (!item->enc) + goto err; + } + + if (auth && auth_len > 0) { + item->auth = os_memdup(auth, auth_len); + item->auth_len = auth_len; + if (!item->auth) + goto err; + } + + eloop_register_timeout(ftRRBseqTimeout, 0, wpa_ft_rrb_seq_timeout, + wpa_auth, item); + + seq_req_auth[0].data = item->nonce; + + if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth, NULL, + wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_REQ, + &packet, &packet_len) < 0) { + item = NULL; /* some other seq resp might still accept this */ + goto err; + } + + dl_list_add(&rkh_seq->rx.queue, &item->list); + + wpa_ft_rrb_oui_send(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ, + packet, packet_len); + + os_free(packet); + + return 0; +err: + wpa_printf(MSG_DEBUG, "FT: Failed to send sequence number request"); + if (item) { + os_free(item->auth); + bin_clear_free(item->enc, item->enc_len); + os_free(item); + } + + return -1; +} + + +#define FT_RRB_SEQ_OK 0 +#define FT_RRB_SEQ_DROP 1 +#define FT_RRB_SEQ_DEFER 2 + +static int +wpa_ft_rrb_seq_chk(struct ft_remote_seq *rkh_seq, const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + const char *msgtype, int no_defer) +{ + const u8 *f_seq; + size_t f_seq_len; + const struct ft_rrb_seq *msg_both; + u32 msg_seq, msg_off, rkh_off; + struct os_reltime now; + unsigned int i; + + RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both)); + wpa_hexdump(MSG_DEBUG, "FT: sequence number", f_seq, f_seq_len); + msg_both = (const struct ft_rrb_seq *) f_seq; + + if (rkh_seq->rx.num_last == 0) { + /* first packet from remote */ + goto defer; + } + + if (le_to_host32(msg_both->dom) != rkh_seq->rx.dom) { + /* remote might have rebooted */ + goto defer; + } + + if (os_get_reltime(&now) == 0) { + u32 msg_ts_now_remote, msg_ts_off; + struct os_reltime now_remote; + + os_reltime_sub(&now, &rkh_seq->rx.time_offset, &now_remote); + msg_ts_now_remote = now_remote.sec; + msg_ts_off = le_to_host32(msg_both->ts) - + (msg_ts_now_remote - ftRRBseqTimeout); + if (msg_ts_off > 2 * ftRRBseqTimeout) + goto defer; + } + + msg_seq = le_to_host32(msg_both->seq); + rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx]; + msg_off = msg_seq - rkh_off; + if (msg_off > 0xC0000000) + goto out; /* too old message, drop it */ + + if (msg_off <= 0x40000000) { + for (i = 0; i < rkh_seq->rx.num_last; i++) { + if (rkh_seq->rx.last[i] == msg_seq) + goto out; /* duplicate message, drop it */ + } + + return FT_RRB_SEQ_OK; + } + +defer: + if (no_defer) + goto out; + + wpa_printf(MSG_DEBUG, "FT: Possibly invalid sequence number in %s from " + MACSTR, msgtype, MAC2STR(src_addr)); + + return FT_RRB_SEQ_DEFER; +out: + wpa_printf(MSG_DEBUG, "FT: Invalid sequence number in %s from " MACSTR, + msgtype, MAC2STR(src_addr)); + + return FT_RRB_SEQ_DROP; +} + + +static void +wpa_ft_rrb_seq_accept(struct wpa_authenticator *wpa_auth, + struct ft_remote_seq *rkh_seq, const u8 *src_addr, + const u8 *auth, size_t auth_len, + const char *msgtype) +{ + const u8 *f_seq; + size_t f_seq_len; + const struct ft_rrb_seq *msg_both; + u32 msg_seq, msg_off, min_off, rkh_off; + int minidx = 0; + unsigned int i; + + RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both)); + msg_both = (const struct ft_rrb_seq *) f_seq; + + msg_seq = le_to_host32(msg_both->seq); + + if (rkh_seq->rx.num_last < FT_REMOTE_SEQ_BACKLOG) { + rkh_seq->rx.last[rkh_seq->rx.num_last] = msg_seq; + rkh_seq->rx.num_last++; + return; + } + + rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx]; + for (i = 0; i < rkh_seq->rx.num_last; i++) { + msg_off = rkh_seq->rx.last[i] - rkh_off; + min_off = rkh_seq->rx.last[minidx] - rkh_off; + if (msg_off < min_off && i != rkh_seq->rx.offsetidx) + minidx = i; + } + rkh_seq->rx.last[rkh_seq->rx.offsetidx] = msg_seq; + rkh_seq->rx.offsetidx = minidx; + + return; +out: + /* RRB_GET_AUTH should never fail here as + * wpa_ft_rrb_seq_chk() verified FT_RRB_SEQ presence. */ + wpa_printf(MSG_ERROR, "FT: %s() failed", __func__); +} + + +static int wpa_ft_new_seq(struct ft_remote_seq *rkh_seq, + struct ft_rrb_seq *f_seq) +{ + struct os_reltime now; + + if (os_get_reltime(&now) < 0) + return -1; + + if (!rkh_seq->tx.dom) { + if (random_get_bytes((u8 *) &rkh_seq->tx.seq, + sizeof(rkh_seq->tx.seq))) { + wpa_printf(MSG_ERROR, + "FT: Failed to get random data for sequence number initialization"); + rkh_seq->tx.seq = now.usec; + } + if (random_get_bytes((u8 *) &rkh_seq->tx.dom, + sizeof(rkh_seq->tx.dom))) { + wpa_printf(MSG_ERROR, + "FT: Failed to get random data for sequence number initialization"); + rkh_seq->tx.dom = now.usec; + } + rkh_seq->tx.dom |= 1; + } + + f_seq->dom = host_to_le32(rkh_seq->tx.dom); + f_seq->seq = host_to_le32(rkh_seq->tx.seq); + f_seq->ts = host_to_le32(now.sec); + + rkh_seq->tx.seq++; + + return 0; +} + + struct wpa_ft_pmk_r0_sa { - struct wpa_ft_pmk_r0_sa *next; - u8 pmk_r0[PMK_LEN]; + struct dl_list list; + u8 pmk_r0[PMK_LEN_MAX]; + size_t pmk_r0_len; u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 spa[ETH_ALEN]; int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ - /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ + struct vlan_description *vlan; + os_time_t expiration; /* 0 for no expiration */ + u8 *identity; + size_t identity_len; + u8 *radius_cui; + size_t radius_cui_len; + os_time_t session_timeout; /* 0 for no expiration */ + /* TODO: radius_class, EAP type */ int pmk_r1_pushed; }; struct wpa_ft_pmk_r1_sa { - struct wpa_ft_pmk_r1_sa *next; - u8 pmk_r1[PMK_LEN]; + struct dl_list list; + u8 pmk_r1[PMK_LEN_MAX]; + size_t pmk_r1_len; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; u8 spa[ETH_ALEN]; int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ - /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ + struct vlan_description *vlan; + u8 *identity; + size_t identity_len; + u8 *radius_cui; + size_t radius_cui_len; + os_time_t session_timeout; /* 0 for no expiration */ + /* TODO: radius_class, EAP type */ }; struct wpa_ft_pmk_cache { - struct wpa_ft_pmk_r0_sa *pmk_r0; - struct wpa_ft_pmk_r1_sa *pmk_r1; + struct dl_list pmk_r0; /* struct wpa_ft_pmk_r0_sa */ + struct dl_list pmk_r1; /* struct wpa_ft_pmk_r1_sa */ }; + +static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx); +static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx); + + +static void wpa_ft_free_pmk_r0(struct wpa_ft_pmk_r0_sa *r0) +{ + if (!r0) + return; + + dl_list_del(&r0->list); + eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL); + + os_memset(r0->pmk_r0, 0, PMK_LEN_MAX); + os_free(r0->vlan); + os_free(r0->identity); + os_free(r0->radius_cui); + os_free(r0); +} + + +static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_ft_pmk_r0_sa *r0 = eloop_ctx; + struct os_reltime now; + int expires_in; + int session_timeout; + + os_get_reltime(&now); + + if (!r0) + return; + + expires_in = r0->expiration - now.sec; + session_timeout = r0->session_timeout - now.sec; + /* conditions to remove from cache: + * a) r0->expiration is set and hit + * -or- + * b) r0->session_timeout is set and hit + */ + if ((!r0->expiration || expires_in > 0) && + (!r0->session_timeout || session_timeout > 0)) { + wpa_printf(MSG_ERROR, + "FT: %s() called for non-expired entry %p", + __func__, r0); + eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL); + if (r0->expiration && expires_in > 0) + eloop_register_timeout(expires_in + 1, 0, + wpa_ft_expire_pmk_r0, r0, NULL); + if (r0->session_timeout && session_timeout > 0) + eloop_register_timeout(session_timeout + 1, 0, + wpa_ft_expire_pmk_r0, r0, NULL); + return; + } + + wpa_ft_free_pmk_r0(r0); +} + + +static void wpa_ft_free_pmk_r1(struct wpa_ft_pmk_r1_sa *r1) +{ + if (!r1) + return; + + dl_list_del(&r1->list); + eloop_cancel_timeout(wpa_ft_expire_pmk_r1, r1, NULL); + + os_memset(r1->pmk_r1, 0, PMK_LEN_MAX); + os_free(r1->vlan); + os_free(r1->identity); + os_free(r1->radius_cui); + os_free(r1); +} + + +static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_ft_pmk_r1_sa *r1 = eloop_ctx; + + wpa_ft_free_pmk_r1(r1); +} + + struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void) { struct wpa_ft_pmk_cache *cache; cache = os_zalloc(sizeof(*cache)); + if (cache) { + dl_list_init(&cache->pmk_r0); + dl_list_init(&cache->pmk_r1); + } return cache; } @@ -181,21 +1246,13 @@ void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache) struct wpa_ft_pmk_r0_sa *r0, *r0prev; struct wpa_ft_pmk_r1_sa *r1, *r1prev; - r0 = cache->pmk_r0; - while (r0) { - r0prev = r0; - r0 = r0->next; - os_memset(r0prev->pmk_r0, 0, PMK_LEN); - os_free(r0prev); - } + dl_list_for_each_safe(r0, r0prev, &cache->pmk_r0, + struct wpa_ft_pmk_r0_sa, list) + wpa_ft_free_pmk_r0(r0); - r1 = cache->pmk_r1; - while (r1) { - r1prev = r1; - r1 = r1->next; - os_memset(r1prev->pmk_r1, 0, PMK_LEN); - os_free(r1prev); - } + dl_list_for_each_safe(r1, r1prev, &cache->pmk_r1, + struct wpa_ft_pmk_r1_sa, list) + wpa_ft_free_pmk_r1(r1); os_free(cache); } @@ -203,24 +1260,63 @@ void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache) static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r0, - const u8 *pmk_r0_name, int pairwise) + size_t pmk_r0_len, + const u8 *pmk_r0_name, int pairwise, + const struct vlan_description *vlan, + int expires_in, int session_timeout, + const u8 *identity, size_t identity_len, + const u8 *radius_cui, size_t radius_cui_len) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r0_sa *r0; + struct os_reltime now; - /* TODO: add expiration and limit on number of entries in cache */ + /* TODO: add limit on number of entries in cache */ + os_get_reltime(&now); r0 = os_zalloc(sizeof(*r0)); if (r0 == NULL) return -1; - os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN); + os_memcpy(r0->pmk_r0, pmk_r0, pmk_r0_len); + r0->pmk_r0_len = pmk_r0_len; os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); os_memcpy(r0->spa, spa, ETH_ALEN); r0->pairwise = pairwise; + if (expires_in > 0) + r0->expiration = now.sec + expires_in; + if (vlan && vlan->notempty) { + r0->vlan = os_zalloc(sizeof(*vlan)); + if (!r0->vlan) { + bin_clear_free(r0, sizeof(*r0)); + return -1; + } + *r0->vlan = *vlan; + } + if (identity) { + r0->identity = os_malloc(identity_len); + if (r0->identity) { + os_memcpy(r0->identity, identity, identity_len); + r0->identity_len = identity_len; + } + } + if (radius_cui) { + r0->radius_cui = os_malloc(radius_cui_len); + if (r0->radius_cui) { + os_memcpy(r0->radius_cui, radius_cui, radius_cui_len); + r0->radius_cui_len = radius_cui_len; + } + } + if (session_timeout > 0) + r0->session_timeout = now.sec + session_timeout; - r0->next = cache->pmk_r0; - cache->pmk_r0 = r0; + dl_list_add(&cache->pmk_r0, &r0->list); + if (expires_in > 0) + eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r0, + r0, NULL); + if (session_timeout > 0) + eloop_register_timeout(session_timeout + 1, 0, + wpa_ft_expire_pmk_r0, r0, NULL); return 0; } @@ -228,49 +1324,89 @@ static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth, static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r0_name, - u8 *pmk_r0, int *pairwise) + const struct wpa_ft_pmk_r0_sa **r0_out) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r0_sa *r0; + struct os_reltime now; - r0 = cache->pmk_r0; - while (r0) { + os_get_reltime(&now); + dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) { if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 && os_memcmp_const(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN) == 0) { - os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN); - if (pairwise) - *pairwise = r0->pairwise; + *r0_out = r0; return 0; } - - r0 = r0->next; } + *r0_out = NULL; return -1; } static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r1, - const u8 *pmk_r1_name, int pairwise) + size_t pmk_r1_len, + const u8 *pmk_r1_name, int pairwise, + const struct vlan_description *vlan, + int expires_in, int session_timeout, + const u8 *identity, size_t identity_len, + const u8 *radius_cui, size_t radius_cui_len) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + int max_expires_in = wpa_auth->conf.r1_max_key_lifetime; struct wpa_ft_pmk_r1_sa *r1; + struct os_reltime now; - /* TODO: add expiration and limit on number of entries in cache */ + /* TODO: limit on number of entries in cache */ + os_get_reltime(&now); + + if (max_expires_in && (max_expires_in < expires_in || expires_in == 0)) + expires_in = max_expires_in; r1 = os_zalloc(sizeof(*r1)); if (r1 == NULL) return -1; - os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN); + os_memcpy(r1->pmk_r1, pmk_r1, pmk_r1_len); + r1->pmk_r1_len = pmk_r1_len; os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); os_memcpy(r1->spa, spa, ETH_ALEN); r1->pairwise = pairwise; + if (vlan && vlan->notempty) { + r1->vlan = os_zalloc(sizeof(*vlan)); + if (!r1->vlan) { + bin_clear_free(r1, sizeof(*r1)); + return -1; + } + *r1->vlan = *vlan; + } + if (identity) { + r1->identity = os_malloc(identity_len); + if (r1->identity) { + os_memcpy(r1->identity, identity, identity_len); + r1->identity_len = identity_len; + } + } + if (radius_cui) { + r1->radius_cui = os_malloc(radius_cui_len); + if (r1->radius_cui) { + os_memcpy(r1->radius_cui, radius_cui, radius_cui_len); + r1->radius_cui_len = radius_cui_len; + } + } + if (session_timeout > 0) + r1->session_timeout = now.sec + session_timeout; - r1->next = cache->pmk_r1; - cache->pmk_r1 = r1; + dl_list_add(&cache->pmk_r1, &r1->list); + + if (expires_in > 0) + eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r1, + r1, NULL); + if (session_timeout > 0) + eloop_register_timeout(session_timeout + 1, 0, + wpa_ft_expire_pmk_r1, r1, NULL); return 0; } @@ -278,94 +1414,616 @@ static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth, static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r1_name, - u8 *pmk_r1, int *pairwise) + u8 *pmk_r1, size_t *pmk_r1_len, int *pairwise, + struct vlan_description *vlan, + const u8 **identity, size_t *identity_len, + const u8 **radius_cui, size_t *radius_cui_len, + int *session_timeout) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r1_sa *r1; + struct os_reltime now; + + os_get_reltime(&now); - r1 = cache->pmk_r1; - while (r1) { + dl_list_for_each(r1, &cache->pmk_r1, struct wpa_ft_pmk_r1_sa, list) { if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 && os_memcmp_const(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN) == 0) { - os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN); + os_memcpy(pmk_r1, r1->pmk_r1, r1->pmk_r1_len); + *pmk_r1_len = r1->pmk_r1_len; if (pairwise) *pairwise = r1->pairwise; + if (vlan && r1->vlan) + *vlan = *r1->vlan; + if (vlan && !r1->vlan) + os_memset(vlan, 0, sizeof(*vlan)); + if (identity && identity_len) { + *identity = r1->identity; + *identity_len = r1->identity_len; + } + if (radius_cui && radius_cui_len) { + *radius_cui = r1->radius_cui; + *radius_cui_len = r1->radius_cui_len; + } + if (session_timeout && r1->session_timeout > now.sec) + *session_timeout = r1->session_timeout - + now.sec; + else if (session_timeout && r1->session_timeout) + *session_timeout = 1; + else if (session_timeout) + *session_timeout = 0; return 0; } - - r1 = r1->next; } return -1; } +static int wpa_ft_rrb_init_r0kh_seq(struct ft_remote_r0kh *r0kh) +{ + if (r0kh->seq) + return 0; + + r0kh->seq = os_zalloc(sizeof(*r0kh->seq)); + if (!r0kh->seq) { + wpa_printf(MSG_DEBUG, "FT: Failed to allocate r0kh->seq"); + return -1; + } + + dl_list_init(&r0kh->seq->rx.queue); + + return 0; +} + + +static void wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator *wpa_auth, + const u8 *f_r0kh_id, size_t f_r0kh_id_len, + struct ft_remote_r0kh **r0kh_out, + struct ft_remote_r0kh **r0kh_wildcard) +{ + struct ft_remote_r0kh *r0kh; + + *r0kh_wildcard = NULL; + *r0kh_out = NULL; + + if (wpa_auth->conf.r0kh_list) + r0kh = *wpa_auth->conf.r0kh_list; + else + r0kh = NULL; + for (; r0kh; r0kh = r0kh->next) { + if (r0kh->id_len == 1 && r0kh->id[0] == '*') + *r0kh_wildcard = r0kh; + if (f_r0kh_id && r0kh->id_len == f_r0kh_id_len && + os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) == 0) + *r0kh_out = r0kh; + } + + if (!*r0kh_out && !*r0kh_wildcard) + wpa_printf(MSG_DEBUG, "FT: No matching R0KH found"); + + if (*r0kh_out && wpa_ft_rrb_init_r0kh_seq(*r0kh_out) < 0) + *r0kh_out = NULL; +} + + +static int wpa_ft_rrb_init_r1kh_seq(struct ft_remote_r1kh *r1kh) +{ + if (r1kh->seq) + return 0; + + r1kh->seq = os_zalloc(sizeof(*r1kh->seq)); + if (!r1kh->seq) { + wpa_printf(MSG_DEBUG, "FT: Failed to allocate r1kh->seq"); + return -1; + } + + dl_list_init(&r1kh->seq->rx.queue); + + return 0; +} + + +static void wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator *wpa_auth, + const u8 *f_r1kh_id, + struct ft_remote_r1kh **r1kh_out, + struct ft_remote_r1kh **r1kh_wildcard) +{ + struct ft_remote_r1kh *r1kh; + + *r1kh_wildcard = NULL; + *r1kh_out = NULL; + + if (wpa_auth->conf.r1kh_list) + r1kh = *wpa_auth->conf.r1kh_list; + else + r1kh = NULL; + for (; r1kh; r1kh = r1kh->next) { + if (is_zero_ether_addr(r1kh->addr) && + is_zero_ether_addr(r1kh->id)) + *r1kh_wildcard = r1kh; + if (f_r1kh_id && + os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) == 0) + *r1kh_out = r1kh; + } + + if (!*r1kh_out && !*r1kh_wildcard) + wpa_printf(MSG_DEBUG, "FT: No matching R1KH found"); + + if (*r1kh_out && wpa_ft_rrb_init_r1kh_seq(*r1kh_out) < 0) + *r1kh_out = NULL; +} + + +static int wpa_ft_rrb_check_r0kh(struct wpa_authenticator *wpa_auth, + const u8 *f_r0kh_id, size_t f_r0kh_id_len) +{ + if (f_r0kh_id_len != wpa_auth->conf.r0_key_holder_len || + os_memcmp_const(f_r0kh_id, wpa_auth->conf.r0_key_holder, + f_r0kh_id_len) != 0) + return -1; + + return 0; +} + + +static int wpa_ft_rrb_check_r1kh(struct wpa_authenticator *wpa_auth, + const u8 *f_r1kh_id) +{ + if (os_memcmp_const(f_r1kh_id, wpa_auth->conf.r1_key_holder, + FT_R1KH_ID_LEN) != 0) + return -1; + + return 0; +} + + +static void wpa_ft_rrb_del_r0kh(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct ft_remote_r0kh *r0kh, *prev = NULL; + + if (!wpa_auth->conf.r0kh_list) + return; + + for (r0kh = *wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) { + if (r0kh == timeout_ctx) + break; + prev = r0kh; + } + if (!r0kh) + return; + if (prev) + prev->next = r0kh->next; + else + *wpa_auth->conf.r0kh_list = r0kh->next; + if (r0kh->seq) + wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0); + os_free(r0kh->seq); + os_free(r0kh); +} + + +static void wpa_ft_rrb_r0kh_replenish(struct wpa_authenticator *wpa_auth, + struct ft_remote_r0kh *r0kh, int timeout) +{ + if (timeout > 0) + eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r0kh, + wpa_auth, r0kh); +} + + +static void wpa_ft_rrb_r0kh_timeout(struct wpa_authenticator *wpa_auth, + struct ft_remote_r0kh *r0kh, int timeout) +{ + eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, r0kh); + + if (timeout > 0) + eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh, + wpa_auth, r0kh); +} + + +static struct ft_remote_r0kh * +wpa_ft_rrb_add_r0kh(struct wpa_authenticator *wpa_auth, + struct ft_remote_r0kh *r0kh_wildcard, + const u8 *src_addr, const u8 *r0kh_id, size_t id_len, + int timeout) +{ + struct ft_remote_r0kh *r0kh; + + if (!wpa_auth->conf.r0kh_list) + return NULL; + + r0kh = os_zalloc(sizeof(*r0kh)); + if (!r0kh) + return NULL; + + if (src_addr) + os_memcpy(r0kh->addr, src_addr, sizeof(r0kh->addr)); + + if (id_len > FT_R0KH_ID_MAX_LEN) + id_len = FT_R0KH_ID_MAX_LEN; + os_memcpy(r0kh->id, r0kh_id, id_len); + r0kh->id_len = id_len; + + os_memcpy(r0kh->key, r0kh_wildcard->key, sizeof(r0kh->key)); + + r0kh->next = *wpa_auth->conf.r0kh_list; + *wpa_auth->conf.r0kh_list = r0kh; + + if (timeout > 0) + eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh, + wpa_auth, r0kh); + + if (wpa_ft_rrb_init_r0kh_seq(r0kh) < 0) + return NULL; + + return r0kh; +} + + +static void wpa_ft_rrb_del_r1kh(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct ft_remote_r1kh *r1kh, *prev = NULL; + + if (!wpa_auth->conf.r1kh_list) + return; + + for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) { + if (r1kh == timeout_ctx) + break; + prev = r1kh; + } + if (!r1kh) + return; + if (prev) + prev->next = r1kh->next; + else + *wpa_auth->conf.r1kh_list = r1kh->next; + if (r1kh->seq) + wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0); + os_free(r1kh->seq); + os_free(r1kh); +} + + +static void wpa_ft_rrb_r1kh_replenish(struct wpa_authenticator *wpa_auth, + struct ft_remote_r1kh *r1kh, int timeout) +{ + if (timeout > 0) + eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r1kh, + wpa_auth, r1kh); +} + + +static struct ft_remote_r1kh * +wpa_ft_rrb_add_r1kh(struct wpa_authenticator *wpa_auth, + struct ft_remote_r1kh *r1kh_wildcard, + const u8 *src_addr, const u8 *r1kh_id, int timeout) +{ + struct ft_remote_r1kh *r1kh; + + if (!wpa_auth->conf.r1kh_list) + return NULL; + + r1kh = os_zalloc(sizeof(*r1kh)); + if (!r1kh) + return NULL; + + os_memcpy(r1kh->addr, src_addr, sizeof(r1kh->addr)); + os_memcpy(r1kh->id, r1kh_id, sizeof(r1kh->id)); + os_memcpy(r1kh->key, r1kh_wildcard->key, sizeof(r1kh->key)); + r1kh->next = *wpa_auth->conf.r1kh_list; + *wpa_auth->conf.r1kh_list = r1kh; + + if (timeout > 0) + eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r1kh, + wpa_auth, r1kh); + + if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0) + return NULL; + + return r1kh; +} + + +void wpa_ft_sta_deinit(struct wpa_state_machine *sm) +{ + eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL); +} + + +static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth) +{ + struct ft_remote_r0kh *r0kh; + struct ft_remote_r1kh *r1kh; + + eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, wpa_auth, ELOOP_ALL_CTX); + + if (wpa_auth->conf.r0kh_list) + r0kh = *wpa_auth->conf.r0kh_list; + else + r0kh = NULL; + for (; r0kh; r0kh = r0kh->next) { + if (!r0kh->seq) + continue; + wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0); + os_free(r0kh->seq); + r0kh->seq = NULL; + } + + if (wpa_auth->conf.r1kh_list) + r1kh = *wpa_auth->conf.r1kh_list; + else + r1kh = NULL; + for (; r1kh; r1kh = r1kh->next) { + if (!r1kh->seq) + continue; + wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0); + os_free(r1kh->seq); + r1kh->seq = NULL; + } +} + + +static void wpa_ft_deinit_rkh_tmp(struct wpa_authenticator *wpa_auth) +{ + struct ft_remote_r0kh *r0kh, *r0kh_next, *r0kh_prev = NULL; + struct ft_remote_r1kh *r1kh, *r1kh_next, *r1kh_prev = NULL; + + if (wpa_auth->conf.r0kh_list) + r0kh = *wpa_auth->conf.r0kh_list; + else + r0kh = NULL; + while (r0kh) { + r0kh_next = r0kh->next; + if (eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, + r0kh) > 0) { + if (r0kh_prev) + r0kh_prev->next = r0kh_next; + else + *wpa_auth->conf.r0kh_list = r0kh_next; + os_free(r0kh); + } else { + r0kh_prev = r0kh; + } + r0kh = r0kh_next; + } + + if (wpa_auth->conf.r1kh_list) + r1kh = *wpa_auth->conf.r1kh_list; + else + r1kh = NULL; + while (r1kh) { + r1kh_next = r1kh->next; + if (eloop_cancel_timeout(wpa_ft_rrb_del_r1kh, wpa_auth, + r1kh) > 0) { + if (r1kh_prev) + r1kh_prev->next = r1kh_next; + else + *wpa_auth->conf.r1kh_list = r1kh_next; + os_free(r1kh); + } else { + r1kh_prev = r1kh; + } + r1kh = r1kh_next; + } +} + + +void wpa_ft_deinit(struct wpa_authenticator *wpa_auth) +{ + wpa_ft_deinit_seq(wpa_auth); + wpa_ft_deinit_rkh_tmp(wpa_auth); +} + + +static void wpa_ft_block_r0kh(struct wpa_authenticator *wpa_auth, + const u8 *f_r0kh_id, size_t f_r0kh_id_len) +{ + struct ft_remote_r0kh *r0kh, *r0kh_wildcard; + + if (!wpa_auth->conf.rkh_neg_timeout) + return; + + wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, + &r0kh, &r0kh_wildcard); + + if (!r0kh_wildcard) { + /* r0kh removed after neg_timeout and might need re-adding */ + return; + } + + wpa_hexdump(MSG_DEBUG, "FT: Blacklist R0KH-ID", + f_r0kh_id, f_r0kh_id_len); + + if (r0kh) { + wpa_ft_rrb_r0kh_timeout(wpa_auth, r0kh, + wpa_auth->conf.rkh_neg_timeout); + os_memset(r0kh->addr, 0, ETH_ALEN); + } else + wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, NULL, f_r0kh_id, + f_r0kh_id_len, + wpa_auth->conf.rkh_neg_timeout); +} + + +static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_state_machine *sm = eloop_ctx; + + wpa_printf(MSG_DEBUG, "FT: Timeout pending pull request for " MACSTR, + MAC2STR(sm->addr)); + if (sm->ft_pending_pull_left_retries <= 0) + wpa_ft_block_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len); + + /* cancel multiple timeouts */ + eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL); + ft_finish_pull(sm); +} + + static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len, const u8 *pmk_r0_name) { - struct ft_remote_r0kh *r0kh; - struct ft_r0kh_r1kh_pull_frame frame, f; + struct ft_remote_r0kh *r0kh, *r0kh_wildcard; + u8 *packet = NULL; + const u8 *key, *f_r1kh_id = sm->wpa_auth->conf.r1_key_holder; + size_t packet_len, key_len; + struct ft_rrb_seq f_seq; + int tsecs, tusecs, first; + struct wpabuf *ft_pending_req_ies; + int r0kh_timeout; + struct tlv_list req_enc[] = { + { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN, + .data = pmk_r0_name }, + { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN, + .data = sm->addr }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + struct tlv_list req_auth[] = { + { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN, + .data = sm->ft_pending_pull_nonce }, + { .type = FT_RRB_SEQ, .len = sizeof(f_seq), + .data = (u8 *) &f_seq }, + { .type = FT_RRB_R0KH_ID, .len = sm->r0kh_id_len, + .data = sm->r0kh_id }, + { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN, + .data = f_r1kh_id }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; - r0kh = sm->wpa_auth->conf.r0kh_list; - while (r0kh) { - if (r0kh->id_len == sm->r0kh_id_len && - os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) == - 0) - break; - r0kh = r0kh->next; + if (sm->ft_pending_pull_left_retries <= 0) + return -1; + first = sm->ft_pending_pull_left_retries == + sm->wpa_auth->conf.rkh_pull_retries; + sm->ft_pending_pull_left_retries--; + + wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len, + &r0kh, &r0kh_wildcard); + + /* Keep r0kh sufficiently long in the list for seq num check */ + r0kh_timeout = sm->wpa_auth->conf.rkh_pull_timeout / 1000 + + 1 + ftRRBseqTimeout; + if (r0kh) { + wpa_ft_rrb_r0kh_replenish(sm->wpa_auth, r0kh, r0kh_timeout); + } else if (r0kh_wildcard) { + wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID"); + /* r0kh->addr: updated by SEQ_RESP and wpa_ft_expire_pull */ + r0kh = wpa_ft_rrb_add_r0kh(sm->wpa_auth, r0kh_wildcard, + r0kh_wildcard->addr, + sm->r0kh_id, sm->r0kh_id_len, + r0kh_timeout); } if (r0kh == NULL) { wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); return -1; } + if (is_zero_ether_addr(r0kh->addr)) { + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID is blacklisted", + sm->r0kh_id, sm->r0kh_id_len); + return -1; + } + if (os_memcmp(r0kh->addr, sm->wpa_auth->addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, + "FT: R0KH-ID points to self - no matching key available"); + return -1; + } + + key = r0kh->key; + key_len = sizeof(r0kh->key); wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH " "address " MACSTR, MAC2STR(r0kh->addr)); - os_memset(&frame, 0, sizeof(frame)); - frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; - frame.packet_type = FT_PACKET_R0KH_R1KH_PULL; - frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN); - os_memcpy(frame.ap_address, sm->wpa_auth->addr, ETH_ALEN); + if (r0kh->seq->rx.num_last == 0) { + /* A sequence request will be sent out anyway when pull + * response is received. Send it out now to avoid one RTT. */ + wpa_ft_rrb_seq_req(sm->wpa_auth, r0kh->seq, r0kh->addr, + r0kh->id, r0kh->id_len, f_r1kh_id, key, + key_len, NULL, 0, NULL, 0, NULL); + } - /* aes_wrap() does not support inplace encryption, so use a temporary - * buffer for the data. */ - if (random_get_bytes(f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN)) { + if (first && + random_get_bytes(sm->ft_pending_pull_nonce, FT_RRB_NONCE_LEN) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "nonce"); return -1; } - os_memcpy(sm->ft_pending_pull_nonce, f.nonce, - FT_R0KH_R1KH_PULL_NONCE_LEN); - os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); - os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); - os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN); - os_memset(f.pad, 0, sizeof(f.pad)); - if (aes_wrap(r0kh->key, sizeof(r0kh->key), - (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, - f.nonce, frame.nonce) < 0) + if (wpa_ft_new_seq(r0kh->seq, &f_seq) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); + return -1; + } + + if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth, NULL, + sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL, + &packet, &packet_len) < 0) return -1; + ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len); wpabuf_free(sm->ft_pending_req_ies); - sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len); - if (sm->ft_pending_req_ies == NULL) + sm->ft_pending_req_ies = ft_pending_req_ies; + if (!sm->ft_pending_req_ies) { + os_free(packet); return -1; + } + + tsecs = sm->wpa_auth->conf.rkh_pull_timeout / 1000; + tusecs = (sm->wpa_auth->conf.rkh_pull_timeout % 1000) * 1000; + eloop_register_timeout(tsecs, tusecs, wpa_ft_expire_pull, sm, NULL); - wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame)); + wpa_ft_rrb_oui_send(sm->wpa_auth, r0kh->addr, FT_PACKET_R0KH_R1KH_PULL, + packet, packet_len); + + os_free(packet); return 0; } +int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, + const u8 *pmk_r0, const u8 *pmk_r0_name) +{ + int expires_in = sm->wpa_auth->conf.r0_key_lifetime; + struct vlan_description vlan; + const u8 *identity, *radius_cui; + size_t identity_len, radius_cui_len; + int session_timeout; + size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ? + SHA384_MAC_LEN : PMK_LEN; + + if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) { + wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR, + MAC2STR(sm->addr)); + return -1; + } + + identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity); + radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr, + &radius_cui); + session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr); + + return wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len, + pmk_r0_name, sm->pairwise, &vlan, expires_in, + session_timeout, identity, identity_len, + radius_cui, radius_cui_len); +} + + int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, struct wpa_ptk *ptk) { - u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; - u8 pmk_r1[PMK_LEN]; + u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN]; + size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ? + SHA384_MAC_LEN : PMK_LEN; + size_t pmk_r1_len = pmk_r0_len; + u8 pmk_r1[PMK_LEN_MAX]; u8 ptk_name[WPA_PMK_NAME_LEN]; const u8 *mdid = sm->wpa_auth->conf.mobility_domain; const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder; @@ -373,6 +2031,12 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder; const u8 *ssid = sm->wpa_auth->conf.ssid; size_t ssid_len = sm->wpa_auth->conf.ssid_len; + int psk_local = sm->wpa_auth->conf.ft_psk_generate_local; + int expires_in = sm->wpa_auth->conf.r0_key_lifetime; + struct vlan_description vlan; + const u8 *identity, *radius_cui; + size_t identity_len, radius_cui_len; + int session_timeout; if (sm->xxkey_len == 0) { wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " @@ -380,23 +2044,45 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, return -1; } - wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid, - r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN); + if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) { + wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR, + MAC2STR(sm->addr)); + return -1; + } + + identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity); + radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr, + &radius_cui); + session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr); + + if (wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid, + r0kh, r0kh_len, sm->addr, + pmk_r0, pmk_r0_name, + wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len); wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN); - wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name, - sm->pairwise); + if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) + wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len, + pmk_r0_name, + sm->pairwise, &vlan, expires_in, + session_timeout, identity, identity_len, + radius_cui, radius_cui_len); - wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr, - pmk_r1, sm->pmk_r1_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN); + if (wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr, + pmk_r1, sm->pmk_r1_name) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); - wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name, - sm->pairwise); + if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) + wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_len, + sm->pmk_r1_name, sm->pairwise, &vlan, + expires_in, session_timeout, identity, + identity_len, radius_cui, radius_cui_len); - return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, - sm->wpa_auth->addr, sm->pmk_r1_name, + return wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce, + sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name, ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise); } @@ -404,9 +2090,9 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, 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) + if (wpa_auth->cb->get_seqnum == NULL) return -1; - return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); + return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq); } @@ -418,6 +2104,16 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) const u8 *key; size_t key_len; u8 keybuf[32]; + const u8 *kek; + size_t kek_len; + + if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + kek = sm->PTK.kek2; + kek_len = sm->PTK.kek2_len; + } else { + kek = sm->PTK.kek; + kek_len = sm->PTK.kek_len; + } key_len = gsm->GTK_len; if (key_len > sizeof(keybuf)) @@ -456,8 +2152,10 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03); subelem[4] = gsm->GTK_len; wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5); - if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, key_len / 8, key, - subelem + 13)) { + if (aes_wrap(kek, kek_len, key_len / 8, key, subelem + 13)) { + wpa_printf(MSG_DEBUG, + "FT: GTK subelem encryption failed: kek_len=%d", + (int) kek_len); os_free(subelem); return NULL; } @@ -473,10 +2171,23 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) u8 *subelem, *pos; struct wpa_group *gsm = sm->group; size_t subelem_len; + const u8 *kek; + size_t kek_len; + size_t igtk_len; + + if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + kek = sm->PTK.kek2; + kek_len = sm->PTK.kek2_len; + } else { + kek = sm->PTK.kek; + kek_len = sm->PTK.kek_len; + } + + igtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher); /* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] | * Key[16+8] */ - subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8; + subelem_len = 1 + 1 + 2 + 6 + 1 + igtk_len + 8; subelem = os_zalloc(subelem_len); if (subelem == NULL) return NULL; @@ -488,9 +2199,12 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) pos += 2; wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos); pos += 6; - *pos++ = WPA_IGTK_LEN; - if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, WPA_IGTK_LEN / 8, + *pos++ = igtk_len; + if (aes_wrap(kek, kek_len, igtk_len / 8, gsm->IGTK[gsm->GN_igtk - 4], pos)) { + wpa_printf(MSG_DEBUG, + "FT: IGTK subelem encryption failed: kek_len=%d", + (int) kek_len); os_free(subelem); return NULL; } @@ -637,17 +2351,21 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, const u8 *req_ies, size_t req_ies_len) { u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL; + u8 *fte_mic, *elem_count; size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0; int res; struct wpa_auth_config *conf; - struct rsn_ftie *_ftie; struct wpa_ft_ies parse; u8 *ric_start; u8 *anonce, *snonce; + const u8 *kck; + size_t kck_len; + int use_sha384; if (sm == NULL) return pos; + use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); conf = &sm->wpa_auth->conf; if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt)) @@ -662,7 +2380,7 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, */ res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name); if (res < 0) - return pos; + return NULL; rsnie = pos; rsnie_len = res; pos += res; @@ -671,7 +2389,7 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, /* Mobility Domain Information */ res = wpa_write_mdie(conf, pos, end - pos); if (res < 0) - return pos; + return NULL; mdie = pos; mdie_len = res; pos += res; @@ -679,6 +2397,11 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, /* Fast BSS Transition Information */ if (auth_alg == WLAN_AUTH_FT) { subelem = wpa_ft_gtk_subelem(sm, &subelem_len); + if (!subelem) { + wpa_printf(MSG_DEBUG, + "FT: Failed to add GTK subelement"); + return NULL; + } r0kh_id = sm->r0kh_id; r0kh_id_len = sm->r0kh_id_len; anonce = sm->ANonce; @@ -690,14 +2413,16 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, u8 *nbuf; igtk = wpa_ft_igtk_subelem(sm, &igtk_len); if (igtk == NULL) { + wpa_printf(MSG_DEBUG, + "FT: Failed to add IGTK subelement"); os_free(subelem); - return pos; + return NULL; } nbuf = os_realloc(subelem, subelem_len + igtk_len); if (nbuf == NULL) { os_free(subelem); os_free(igtk); - return pos; + return NULL; } subelem = nbuf; os_memcpy(subelem + subelem_len, igtk, igtk_len); @@ -711,44 +2436,66 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, anonce = NULL; snonce = NULL; } - res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos, - end - pos, subelem, subelem_len); + res = wpa_write_ftie(conf, use_sha384, r0kh_id, r0kh_id_len, + anonce, snonce, pos, end - pos, + subelem, subelem_len); os_free(subelem); if (res < 0) - return pos; + return NULL; ftie = pos; ftie_len = res; pos += res; - _ftie = (struct rsn_ftie *) (ftie + 2); + if (use_sha384) { + struct rsn_ftie_sha384 *_ftie = + (struct rsn_ftie_sha384 *) (ftie + 2); + + fte_mic = _ftie->mic; + elem_count = &_ftie->mic_control[1]; + } else { + struct rsn_ftie *_ftie = (struct rsn_ftie *) (ftie + 2); + + fte_mic = _ftie->mic; + elem_count = &_ftie->mic_control[1]; + } if (auth_alg == WLAN_AUTH_FT) - _ftie->mic_control[1] = 3; /* Information element count */ + *elem_count = 3; /* Information element count */ ric_start = pos; - if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) { + if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse, use_sha384) == 0 + && parse.ric) { pos = wpa_ft_process_ric(sm, pos, end, parse.ric, parse.ric_len); if (auth_alg == WLAN_AUTH_FT) - _ftie->mic_control[1] += + *elem_count += ieee802_11_ie_count(ric_start, pos - ric_start); } if (ric_start == pos) ric_start = NULL; + if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + kck = sm->PTK.kck2; + kck_len = sm->PTK.kck2_len; + } else { + kck = sm->PTK.kck; + kck_len = sm->PTK.kck_len; + } if (auth_alg == WLAN_AUTH_FT && - wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr, - sm->wpa_auth->addr, 6, + wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 6, mdie, mdie_len, ftie, ftie_len, rsnie, rsnie_len, ric_start, ric_start ? pos - ric_start : 0, - _ftie->mic) < 0) + fte_mic) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); + return NULL; + } os_free(sm->assoc_resp_ftie); sm->assoc_resp_ftie = os_malloc(ftie_len); - if (sm->assoc_resp_ftie) - os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len); + if (!sm->assoc_resp_ftie) + return NULL; + os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len); return pos; } @@ -759,10 +2506,10 @@ static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len) { - if (wpa_auth->cb.set_key == NULL) + 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); + return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx, + key, key_len); } @@ -804,20 +2551,219 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm) } +/* Derive PMK-R1 from PSK, check all available PSK */ +static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm, + const u8 *req_pmk_r1_name, + u8 *out_pmk_r1, int *out_pairwise, + struct vlan_description *out_vlan, + const u8 **out_identity, size_t *out_identity_len, + const u8 **out_radius_cui, + size_t *out_radius_cui_len, + int *out_session_timeout) +{ + const u8 *pmk = NULL; + u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; + struct wpa_authenticator *wpa_auth = sm->wpa_auth; + const u8 *mdid = wpa_auth->conf.mobility_domain; + const u8 *r0kh = sm->r0kh_id; + size_t r0kh_len = sm->r0kh_id_len; + const u8 *r1kh = wpa_auth->conf.r1_key_holder; + const u8 *ssid = wpa_auth->conf.ssid; + size_t ssid_len = wpa_auth->conf.ssid_len; + int pairwise; + + pairwise = sm->pairwise; + + for (;;) { + pmk = wpa_ft_get_psk(wpa_auth, sm->addr, sm->p2p_dev_addr, + pmk); + if (pmk == NULL) + break; + + if (wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh, + r0kh_len, sm->addr, + pmk_r0, pmk_r0_name, 0) < 0 || + wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, r1kh, + sm->addr, pmk_r1, pmk_r1_name) < 0 || + os_memcmp_const(pmk_r1_name, req_pmk_r1_name, + WPA_PMK_NAME_LEN) != 0) + continue; + + /* We found a PSK that matches the requested pmk_r1_name */ + wpa_printf(MSG_DEBUG, + "FT: Found PSK to generate PMK-R1 locally"); + os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN); + if (out_pairwise) + *out_pairwise = pairwise; + if (out_vlan && + wpa_ft_get_vlan(sm->wpa_auth, sm->addr, out_vlan) < 0) { + wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " + MACSTR, MAC2STR(sm->addr)); + return -1; + } + + if (out_identity && out_identity_len) { + *out_identity_len = wpa_ft_get_identity( + sm->wpa_auth, sm->addr, out_identity); + } + + if (out_radius_cui && out_radius_cui_len) { + *out_radius_cui_len = wpa_ft_get_radius_cui( + sm->wpa_auth, sm->addr, out_radius_cui); + } + + if (out_session_timeout) { + *out_session_timeout = wpa_ft_get_session_timeout( + sm->wpa_auth, sm->addr); + } + + return 0; + } + + wpa_printf(MSG_DEBUG, + "FT: Did not find PSK to generate PMK-R1 locally"); + return -1; +} + + +/* Detect the configuration the station asked for. + * Required to detect FT-PSK and pairwise cipher. + */ +static int wpa_ft_set_key_mgmt(struct wpa_state_machine *sm, + struct wpa_ft_ies *parse) +{ + int key_mgmt, ciphers; + + if (sm->wpa_key_mgmt) + return 0; + + key_mgmt = parse->key_mgmt & sm->wpa_auth->conf.wpa_key_mgmt; + if (!key_mgmt) { + wpa_printf(MSG_DEBUG, "FT: Invalid key mgmt (0x%x) from " + MACSTR, parse->key_mgmt, MAC2STR(sm->addr)); + return -1; + } + if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; +#ifdef CONFIG_SHA384 + else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; +#endif /* CONFIG_SHA384 */ + else if (key_mgmt & WPA_KEY_MGMT_FT_PSK) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK; +#ifdef CONFIG_FILS + else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256; + else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384; +#endif /* CONFIG_FILS */ + ciphers = parse->pairwise_cipher & sm->wpa_auth->conf.rsn_pairwise; + if (!ciphers) { + wpa_printf(MSG_DEBUG, "FT: Invalid pairwise cipher (0x%x) from " + MACSTR, + parse->pairwise_cipher, MAC2STR(sm->addr)); + return -1; + } + sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0); + + return 0; +} + + +static int wpa_ft_local_derive_pmk_r1(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *r0kh_id, size_t r0kh_id_len, + const u8 *req_pmk_r0_name, + const u8 *req_pmk_r1_name, + u8 *out_pmk_r1, int *out_pairwise, + struct vlan_description *vlan, + const u8 **identity, size_t *identity_len, + const u8 **radius_cui, + size_t *radius_cui_len, + int *out_session_timeout) +{ + struct wpa_auth_config *conf = &wpa_auth->conf; + const struct wpa_ft_pmk_r0_sa *r0; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + int expires_in = 0; + int session_timeout = 0; + struct os_reltime now; + + if (conf->r0_key_holder_len != r0kh_id_len || + os_memcmp(conf->r0_key_holder, r0kh_id, conf->r0_key_holder_len) != + 0) + return -1; /* not our R0KH-ID */ + + wpa_printf(MSG_DEBUG, "FT: STA R0KH-ID matching local configuration"); + if (wpa_ft_fetch_pmk_r0(sm->wpa_auth, sm->addr, req_pmk_r0_name, &r0) < + 0) + return -1; /* no matching PMKR0Name in local cache */ + + wpa_printf(MSG_DEBUG, "FT: Requested PMKR0Name found in local cache"); + + if (wpa_derive_pmk_r1(r0->pmk_r0, r0->pmk_r0_len, r0->pmk_r0_name, + conf->r1_key_holder, + sm->addr, out_pmk_r1, pmk_r1_name) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", out_pmk_r1, r0->pmk_r0_len); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); + + os_get_reltime(&now); + if (r0->expiration) + expires_in = r0->expiration - now.sec; + + if (r0->session_timeout) + session_timeout = r0->session_timeout - now.sec; + + wpa_ft_store_pmk_r1(wpa_auth, sm->addr, out_pmk_r1, r0->pmk_r0_len, + pmk_r1_name, + sm->pairwise, r0->vlan, expires_in, session_timeout, + r0->identity, r0->identity_len, + r0->radius_cui, r0->radius_cui_len); + + *out_pairwise = sm->pairwise; + if (vlan) { + if (r0->vlan) + *vlan = *r0->vlan; + else + os_memset(vlan, 0, sizeof(*vlan)); + } + + if (identity && identity_len) { + *identity = r0->identity; + *identity_len = r0->identity_len; + } + + if (radius_cui && radius_cui_len) { + *radius_cui = r0->radius_cui; + *radius_cui_len = r0->radius_cui_len; + } + + *out_session_timeout = session_timeout; + + return 0; +} + + static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len, u8 **resp_ies, size_t *resp_ies_len) { struct rsn_mdie *mdie; - struct rsn_ftie *ftie; - u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN_MAX], pmk_r1_name[WPA_PMK_NAME_LEN]; u8 ptk_name[WPA_PMK_NAME_LEN]; struct wpa_auth_config *conf; struct wpa_ft_ies parse; size_t buflen; int ret; u8 *pos, *end; - int pairwise; + int pairwise, session_timeout = 0; + struct vlan_description vlan; + const u8 *identity, *radius_cui; + size_t identity_len = 0, radius_cui_len = 0; + int use_sha384; + size_t pmk_r1_len; *resp_ies = NULL; *resp_ies_len = 0; @@ -828,10 +2774,12 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs", ies, ies_len); - if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) { wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } + use_sha384 = wpa_key_mgmt_sha384(parse.key_mgmt); + pmk_r1_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN; mdie = (struct rsn_mdie *) parse.mdie; if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || @@ -842,13 +2790,27 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, return WLAN_STATUS_INVALID_MDIE; } - ftie = (struct rsn_ftie *) parse.ftie; - if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { - wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); - return WLAN_STATUS_INVALID_FTIE; - } + if (use_sha384) { + struct rsn_ftie_sha384 *ftie; + + ftie = (struct rsn_ftie_sha384 *) parse.ftie; + if (!ftie || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } + + os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN); + } else { + struct rsn_ftie *ftie; - os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN); + ftie = (struct rsn_ftie *) parse.ftie; + if (!ftie || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } + + os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN); + } if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID"); @@ -865,26 +2827,58 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, return WLAN_STATUS_INVALID_PMKID; } + if (wpa_ft_set_key_mgmt(sm, &parse) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name", parse.rsn_pmkid, WPA_PMK_NAME_LEN); - wpa_derive_pmk_r1_name(parse.rsn_pmkid, - sm->wpa_auth->conf.r1_key_holder, sm->addr, - pmk_r1_name); + if (wpa_derive_pmk_r1_name(parse.rsn_pmkid, + sm->wpa_auth->conf.r1_key_holder, sm->addr, + pmk_r1_name, use_sha384) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); - if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1, - &pairwise) < 0) { + if (conf->ft_psk_generate_local && + wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) { + if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise, + &vlan, &identity, &identity_len, + &radius_cui, &radius_cui_len, + &session_timeout) < 0) + return WLAN_STATUS_INVALID_PMKID; + wpa_printf(MSG_DEBUG, + "FT: Generated PMK-R1 for FT-PSK locally"); + } else if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, + pmk_r1, &pmk_r1_len, &pairwise, &vlan, + &identity, &identity_len, &radius_cui, + &radius_cui_len, &session_timeout) < 0) { + wpa_printf(MSG_DEBUG, + "FT: No PMK-R1 available in local cache for the requested PMKR1Name"); + if (wpa_ft_local_derive_pmk_r1(sm->wpa_auth, sm, + parse.r0kh_id, parse.r0kh_id_len, + parse.rsn_pmkid, + pmk_r1_name, pmk_r1, &pairwise, + &vlan, &identity, &identity_len, + &radius_cui, &radius_cui_len, + &session_timeout) == 0) { + wpa_printf(MSG_DEBUG, + "FT: Generated PMK-R1 based on local PMK-R0"); + goto pmk_r1_derived; + } + if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) { - wpa_printf(MSG_DEBUG, "FT: Did not have matching " - "PMK-R1 and unknown R0KH-ID"); + wpa_printf(MSG_DEBUG, + "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH"); return WLAN_STATUS_INVALID_PMKID; } return -1; /* Status pending */ + } else { + wpa_printf(MSG_DEBUG, "FT: Found PMKR1Name from local cache"); } - wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN); +pmk_r1_derived: + wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len); sm->pmk_r1_name_valid = 1; os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); @@ -899,8 +2893,8 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce", sm->ANonce, WPA_NONCE_LEN); - if (wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, - sm->wpa_auth->addr, pmk_r1_name, + if (wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce, + sm->addr, sm->wpa_auth->addr, pmk_r1_name, &sm->PTK, ptk_name, sm->wpa_key_mgmt, pairwise) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -910,44 +2904,51 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, sm->tk_already_set = FALSE; wpa_ft_install_ptk(sm); + if (wpa_ft_set_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to configure VLAN"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + if (wpa_ft_set_identity(sm->wpa_auth, sm->addr, + identity, identity_len) < 0 || + wpa_ft_set_radius_cui(sm->wpa_auth, sm->addr, + radius_cui, radius_cui_len) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to configure identity/CUI"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + wpa_ft_set_session_timeout(sm->wpa_auth, sm->addr, session_timeout); + buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + 2 + FT_R1KH_ID_LEN + 200; *resp_ies = os_zalloc(buflen); - if (*resp_ies == NULL) { - return WLAN_STATUS_UNSPECIFIED_FAILURE; - } + if (*resp_ies == NULL) + goto fail; pos = *resp_ies; end = *resp_ies + buflen; ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid); - if (ret < 0) { - os_free(*resp_ies); - *resp_ies = NULL; - return WLAN_STATUS_UNSPECIFIED_FAILURE; - } + if (ret < 0) + goto fail; pos += ret; ret = wpa_write_mdie(conf, pos, end - pos); - if (ret < 0) { - os_free(*resp_ies); - *resp_ies = NULL; - return WLAN_STATUS_UNSPECIFIED_FAILURE; - } + if (ret < 0) + goto fail; pos += ret; - ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len, + ret = wpa_write_ftie(conf, use_sha384, parse.r0kh_id, parse.r0kh_id_len, sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0); - if (ret < 0) { - os_free(*resp_ies); - *resp_ies = NULL; - return WLAN_STATUS_UNSPECIFIED_FAILURE; - } + if (ret < 0) + goto fail; pos += ret; *resp_ies_len = pos - *resp_ies; return WLAN_STATUS_SUCCESS; +fail: + os_free(*resp_ies); + *resp_ies = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; } @@ -975,6 +2976,7 @@ void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, sm->ft_pending_cb = cb; sm->ft_pending_cb_ctx = ctx; sm->ft_pending_auth_transaction = auth_transaction; + sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries; res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies, &resp_ies_len); if (res < 0) { @@ -998,17 +3000,23 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, { struct wpa_ft_ies parse; struct rsn_mdie *mdie; - struct rsn_ftie *ftie; u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; size_t mic_len = 16; unsigned int count; + const u8 *kck; + size_t kck_len; + int use_sha384; + const u8 *anonce, *snonce, *fte_mic; + u8 fte_elem_count; if (sm == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; + use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); + wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len); - if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } @@ -1039,34 +3047,56 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, return WLAN_STATUS_INVALID_MDIE; } - ftie = (struct rsn_ftie *) parse.ftie; - if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { - wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); - return WLAN_STATUS_INVALID_FTIE; + if (use_sha384) { + struct rsn_ftie_sha384 *ftie; + + ftie = (struct rsn_ftie_sha384 *) parse.ftie; + if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } + + anonce = ftie->anonce; + snonce = ftie->snonce; + fte_elem_count = ftie->mic_control[1]; + fte_mic = ftie->mic; + } else { + struct rsn_ftie *ftie; + + ftie = (struct rsn_ftie *) parse.ftie; + if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } + + anonce = ftie->anonce; + snonce = ftie->snonce; + fte_elem_count = ftie->mic_control[1]; + fte_mic = ftie->mic; } - if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) { + if (os_memcmp(snonce, sm->SNonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", - ftie->snonce, WPA_NONCE_LEN); + snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", sm->SNonce, WPA_NONCE_LEN); - return -1; + return WLAN_STATUS_INVALID_FTIE; } - if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) { + if (os_memcmp(anonce, sm->ANonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received ANonce", - ftie->anonce, WPA_NONCE_LEN); + anonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", sm->ANonce, WPA_NONCE_LEN); - return -1; + return WLAN_STATUS_INVALID_FTIE; } if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); - return -1; + return WLAN_STATUS_INVALID_FTIE; } if (parse.r0kh_id_len != sm->r0kh_id_len || @@ -1078,12 +3108,12 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, parse.r0kh_id, parse.r0kh_id_len); wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); - return -1; + return WLAN_STATUS_INVALID_FTIE; } if (parse.r1kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); - return -1; + return WLAN_STATUS_INVALID_FTIE; } if (os_memcmp_const(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder, @@ -1094,7 +3124,7 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, parse.r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID", sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); - return -1; + return WLAN_STATUS_INVALID_FTIE; } if (parse.rsn_pmkid == NULL || @@ -1102,21 +3132,27 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, { wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in " "RSNIE (pmkid=%d)", !!parse.rsn_pmkid); - return -1; + return WLAN_STATUS_INVALID_PMKID; } count = 3; if (parse.ric) count += ieee802_11_ie_count(parse.ric, parse.ric_len); - if (ftie->mic_control[1] != count) { + if (fte_elem_count != count) { wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " "Control: received %u expected %u", - ftie->mic_control[1], count); - return -1; + fte_elem_count, count); + return WLAN_STATUS_UNSPECIFIED_FAILURE; } - if (wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr, - sm->wpa_auth->addr, 5, + if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + kck = sm->PTK.kck2; + kck_len = sm->PTK.kck2_len; + } else { + kck = sm->PTK.kck; + kck_len = sm->PTK.kck_len; + } + if (wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 5, parse.mdie - 2, parse.mdie_len + 2, parse.ftie - 2, parse.ftie_len + 2, parse.rsn - 2, parse.rsn_len + 2, @@ -1126,12 +3162,12 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, return WLAN_STATUS_UNSPECIFIED_FAILURE; } - if (os_memcmp_const(mic, ftie->mic, mic_len) != 0) { + if (os_memcmp_const(mic, fte_mic, mic_len) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR, MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr)); wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", - ftie->mic, mic_len); + fte_mic, mic_len); wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len); wpa_hexdump(MSG_MSGDUMP, "FT: MDIE", parse.mdie - 2, parse.mdie_len + 2); @@ -1199,6 +3235,11 @@ int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len) wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len); + if (!sm->wpa_auth->conf.ft_over_ds) { + wpa_printf(MSG_DEBUG, "FT: Over-DS option disabled - reject"); + return -1; + } + /* RRB - Forward action frame to the target AP */ frame = os_malloc(sizeof(*frame) + len); if (frame == NULL) @@ -1251,6 +3292,7 @@ static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth, sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb; sm->ft_pending_cb_ctx = sm; os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN); + sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries; res = wpa_ft_process_auth_req(sm, body, len, &resp_ies, &resp_ies_len); if (res < 0) { @@ -1316,112 +3358,431 @@ static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm, } +static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len, + const struct tlv_list *tlvs, + const struct wpa_ft_pmk_r0_sa *pmk_r0, + const u8 *r1kh_id, const u8 *s1kh_id, + const struct tlv_list *tlv_auth, + const u8 *src_addr, u8 type, + u8 **packet, size_t *packet_len) +{ + u8 pmk_r1[PMK_LEN_MAX]; + size_t pmk_r1_len = pmk_r0->pmk_r0_len; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 f_pairwise[sizeof(le16)]; + u8 f_expires_in[sizeof(le16)]; + u8 f_session_timeout[sizeof(le32)]; + int expires_in; + int session_timeout; + struct os_reltime now; + int ret; + struct tlv_list sess_tlv[] = { + { .type = FT_RRB_PMK_R1, .len = pmk_r1_len, + .data = pmk_r1 }, + { .type = FT_RRB_PMK_R1_NAME, .len = sizeof(pmk_r1_name), + .data = pmk_r1_name }, + { .type = FT_RRB_PAIRWISE, .len = sizeof(f_pairwise), + .data = f_pairwise }, + { .type = FT_RRB_EXPIRES_IN, .len = sizeof(f_expires_in), + .data = f_expires_in }, + { .type = FT_RRB_IDENTITY, .len = pmk_r0->identity_len, + .data = pmk_r0->identity }, + { .type = FT_RRB_RADIUS_CUI, .len = pmk_r0->radius_cui_len, + .data = pmk_r0->radius_cui }, + { .type = FT_RRB_SESSION_TIMEOUT, + .len = sizeof(f_session_timeout), + .data = f_session_timeout }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + + if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_len, + pmk_r0->pmk_r0_name, r1kh_id, + s1kh_id, pmk_r1, pmk_r1_name) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 (for peer AP)", + pmk_r1, pmk_r1_len); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name (for peer AP)", + pmk_r1_name, WPA_PMK_NAME_LEN); + WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise); + + os_get_reltime(&now); + if (pmk_r0->expiration > now.sec) + expires_in = pmk_r0->expiration - now.sec; + else if (pmk_r0->expiration) + expires_in = 1; + else + expires_in = 0; + WPA_PUT_LE16(f_expires_in, expires_in); + + if (pmk_r0->session_timeout > now.sec) + session_timeout = pmk_r0->session_timeout - now.sec; + else if (pmk_r0->session_timeout) + session_timeout = 1; + else + session_timeout = 0; + WPA_PUT_LE32(f_session_timeout, session_timeout); + + ret = wpa_ft_rrb_build(key, key_len, tlvs, sess_tlv, tlv_auth, + pmk_r0->vlan, src_addr, type, + packet, packet_len); + + os_memset(pmk_r1, 0, sizeof(pmk_r1)); + + return ret; +} + + static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, const u8 *src_addr, - const u8 *data, size_t data_len) + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer) { - struct ft_r0kh_r1kh_pull_frame f; - const u8 *crypt; - u8 *plain; - struct ft_remote_r1kh *r1kh; - struct ft_r0kh_r1kh_resp_frame resp, r; - u8 pmk_r0[PMK_LEN]; - int pairwise; + const char *msgtype = "pull request"; + u8 *plain = NULL, *packet = NULL; + size_t plain_len = 0, packet_len = 0; + struct ft_remote_r1kh *r1kh, *r1kh_wildcard; + const u8 *key; + size_t key_len; + int seq_ret; + const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id, *f_s1kh_id, *f_pmk_r0_name; + size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len, f_s1kh_id_len; + size_t f_pmk_r0_name_len; + const struct wpa_ft_pmk_r0_sa *r0; + int ret; + struct tlv_list resp[2]; + struct tlv_list resp_auth[5]; + struct ft_rrb_seq f_seq; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull"); - if (data_len < sizeof(f)) - return -1; + RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1); + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len); - r1kh = wpa_auth->conf.r1kh_list; - while (r1kh) { - if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0) - break; - r1kh = r1kh->next; + if (wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len)) { + wpa_printf(MSG_DEBUG, "FT: R0KH-ID mismatch"); + goto out; } - if (r1kh == NULL) { - wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for " - "PMK-R1 pull source address " MACSTR, - MAC2STR(src_addr)); - return -1; + + RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN); + wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id)); + + wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, &r1kh_wildcard); + if (r1kh) { + key = r1kh->key; + key_len = sizeof(r1kh->key); + } else if (r1kh_wildcard) { + wpa_printf(MSG_DEBUG, "FT: Using wildcard R1KH-ID"); + key = r1kh_wildcard->key; + key_len = sizeof(r1kh_wildcard->key); + } else { + goto out; } - crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce); - os_memset(&f, 0, sizeof(f)); - plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce); - /* aes_unwrap() does not support inplace decryption, so use a temporary - * buffer for the data. */ - if (aes_unwrap(r1kh->key, sizeof(r1kh->key), - (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, - crypt, plain) < 0) { - wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " - "request from " MACSTR, MAC2STR(src_addr)); - return -1; + RRB_GET_AUTH(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len); + + seq_ret = FT_RRB_SEQ_DROP; + if (r1kh) + seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len, + auth, auth_len, msgtype, no_defer); + if (!no_defer && r1kh_wildcard && + (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) { + /* wildcard: r1kh-id unknown or changed addr -> do a seq req */ + seq_ret = FT_RRB_SEQ_DEFER; } - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", - f.nonce, sizeof(f.nonce)); - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name", - f.pmk_r0_name, WPA_PMK_NAME_LEN); - wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID=" - MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id)); + if (seq_ret == FT_RRB_SEQ_DROP) + goto out; - os_memset(&resp, 0, sizeof(resp)); - resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; - resp.packet_type = FT_PACKET_R0KH_R1KH_RESP; - resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN); - os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN); + if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len, + src_addr, FT_PACKET_R0KH_R1KH_PULL, + &plain, &plain_len) < 0) + goto out; - /* aes_wrap() does not support inplace encryption, so use a temporary - * buffer for the data. */ - os_memcpy(r.nonce, f.nonce, sizeof(f.nonce)); - os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN); - os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN); - if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0, - &pairwise) < 0) { - wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for " - "PMK-R1 pull"); - return -1; + if (!r1kh) + r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, src_addr, + f_r1kh_id, + wpa_auth->conf.rkh_pos_timeout); + if (!r1kh) + goto out; + + if (seq_ret == FT_RRB_SEQ_DEFER) { + wpa_ft_rrb_seq_req(wpa_auth, r1kh->seq, src_addr, f_r0kh_id, + f_r0kh_id_len, f_r1kh_id, key, key_len, + enc, enc_len, auth, auth_len, + &wpa_ft_rrb_rx_pull); + goto out; } - wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id, - r.pmk_r1, r.pmk_r1_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name, - WPA_PMK_NAME_LEN); - r.pairwise = host_to_le16(pairwise); - os_memset(r.pad, 0, sizeof(r.pad)); + wpa_ft_rrb_seq_accept(wpa_auth, r1kh->seq, src_addr, auth, auth_len, + msgtype); + wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, + wpa_auth->conf.rkh_pos_timeout); - if (aes_wrap(r1kh->key, sizeof(r1kh->key), - (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, - r.nonce, resp.nonce) < 0) { - os_memset(pmk_r0, 0, PMK_LEN); - return -1; + RRB_GET(FT_RRB_PMK_R0_NAME, pmk_r0_name, msgtype, WPA_PMK_NAME_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", f_pmk_r0_name, + f_pmk_r0_name_len); + + RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN); + wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id)); + + if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); + goto out; } - os_memset(pmk_r0, 0, PMK_LEN); + resp[0].type = FT_RRB_S1KH_ID; + resp[0].len = f_s1kh_id_len; + resp[0].data = f_s1kh_id; + resp[1].type = FT_RRB_LAST_EMPTY; + resp[1].len = 0; + resp[1].data = NULL; - wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp)); + resp_auth[0].type = FT_RRB_NONCE; + resp_auth[0].len = f_nonce_len; + resp_auth[0].data = f_nonce; + resp_auth[1].type = FT_RRB_SEQ; + resp_auth[1].len = sizeof(f_seq); + resp_auth[1].data = (u8 *) &f_seq; + resp_auth[2].type = FT_RRB_R0KH_ID; + resp_auth[2].len = f_r0kh_id_len; + resp_auth[2].data = f_r0kh_id; + resp_auth[3].type = FT_RRB_R1KH_ID; + resp_auth[3].len = f_r1kh_id_len; + resp_auth[3].data = f_r1kh_id; + resp_auth[4].type = FT_RRB_LAST_EMPTY; + resp_auth[4].len = 0; + resp_auth[4].data = NULL; + + if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0) { + wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found"); + ret = wpa_ft_rrb_build(key, key_len, resp, NULL, resp_auth, + NULL, wpa_auth->addr, + FT_PACKET_R0KH_R1KH_RESP, + &packet, &packet_len); + } else { + ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id, + f_s1kh_id, resp_auth, wpa_auth->addr, + FT_PACKET_R0KH_R1KH_RESP, + &packet, &packet_len); + } + + if (!ret) + wpa_ft_rrb_oui_send(wpa_auth, src_addr, + FT_PACKET_R0KH_R1KH_RESP, packet, + packet_len); + +out: + os_free(plain); + os_free(packet); return 0; } -static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx) +/* @returns 0 on success + * -1 on error + * -2 if FR_RRB_PAIRWISE is missing + */ +static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, u8 type, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + const char *msgtype, u8 *s1kh_id_out, + int (*cb)(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer)) +{ + u8 *plain = NULL; + size_t plain_len = 0; + struct ft_remote_r0kh *r0kh, *r0kh_wildcard; + const u8 *key; + size_t key_len; + int seq_ret; + const u8 *f_r1kh_id, *f_s1kh_id, *f_r0kh_id; + const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1; + const u8 *f_expires_in; + size_t f_r1kh_id_len, f_s1kh_id_len, f_r0kh_id_len; + const u8 *f_identity, *f_radius_cui; + const u8 *f_session_timeout; + size_t f_pmk_r1_name_len, f_pairwise_len, f_pmk_r1_len; + size_t f_expires_in_len; + size_t f_identity_len, f_radius_cui_len; + size_t f_session_timeout_len; + int pairwise; + int ret = -1; + int expires_in; + int session_timeout; + struct vlan_description vlan; + size_t pmk_r1_len; + + RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1); + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len); + + RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN); + wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id)); + + if (wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id)) { + wpa_printf(MSG_DEBUG, "FT: R1KH-ID mismatch"); + goto out; + } + + wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh, + &r0kh_wildcard); + if (r0kh) { + key = r0kh->key; + key_len = sizeof(r0kh->key); + } else if (r0kh_wildcard) { + wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID"); + key = r0kh_wildcard->key; + key_len = sizeof(r0kh_wildcard->key); + } else { + goto out; + } + + seq_ret = FT_RRB_SEQ_DROP; + if (r0kh) { + seq_ret = wpa_ft_rrb_seq_chk(r0kh->seq, src_addr, enc, enc_len, + auth, auth_len, msgtype, + cb ? 0 : 1); + } + if (cb && r0kh_wildcard && + (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) { + /* wildcard: r0kh-id unknown or changed addr -> do a seq req */ + seq_ret = FT_RRB_SEQ_DEFER; + } + + if (seq_ret == FT_RRB_SEQ_DROP) + goto out; + + if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len, + src_addr, type, &plain, &plain_len) < 0) + goto out; + + if (!r0kh) + r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, src_addr, + f_r0kh_id, f_r0kh_id_len, + wpa_auth->conf.rkh_pos_timeout); + if (!r0kh) + goto out; + + if (seq_ret == FT_RRB_SEQ_DEFER) { + wpa_ft_rrb_seq_req(wpa_auth, r0kh->seq, src_addr, f_r0kh_id, + f_r0kh_id_len, f_r1kh_id, key, key_len, + enc, enc_len, auth, auth_len, cb); + goto out; + } + + wpa_ft_rrb_seq_accept(wpa_auth, r0kh->seq, src_addr, auth, auth_len, + msgtype); + wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, + wpa_auth->conf.rkh_pos_timeout); + + RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN); + wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id)); + + if (s1kh_id_out) + os_memcpy(s1kh_id_out, f_s1kh_id, ETH_ALEN); + + ret = -2; + RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16)); + wpa_hexdump(MSG_DEBUG, "FT: pairwise", f_pairwise, f_pairwise_len); + + ret = -1; + RRB_GET(FT_RRB_PMK_R1_NAME, pmk_r1_name, msgtype, WPA_PMK_NAME_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", + f_pmk_r1_name, WPA_PMK_NAME_LEN); + + pmk_r1_len = PMK_LEN; + if (wpa_ft_rrb_get_tlv(plain, plain_len, FT_RRB_PMK_R1, &f_pmk_r1_len, + &f_pmk_r1) == 0 && + (f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN)) + pmk_r1_len = f_pmk_r1_len; + RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, pmk_r1_len); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, pmk_r1_len); + + pairwise = WPA_GET_LE16(f_pairwise); + + RRB_GET_OPTIONAL(FT_RRB_EXPIRES_IN, expires_in, msgtype, + sizeof(le16)); + if (f_expires_in) + expires_in = WPA_GET_LE16(f_expires_in); + else + expires_in = 0; + + wpa_printf(MSG_DEBUG, "FT: PMK-R1 %s - expires_in=%d", msgtype, + expires_in); + + if (wpa_ft_rrb_get_tlv_vlan(plain, plain_len, &vlan) < 0) { + wpa_printf(MSG_DEBUG, "FT: Cannot parse vlan"); + wpa_ft_rrb_dump(plain, plain_len); + goto out; + } + + wpa_printf(MSG_DEBUG, "FT: vlan %d%s", + le_to_host16(vlan.untagged), vlan.tagged[0] ? "+" : ""); + + RRB_GET_OPTIONAL(FT_RRB_IDENTITY, identity, msgtype, -1); + if (f_identity) + wpa_hexdump_ascii(MSG_DEBUG, "FT: Identity", f_identity, + f_identity_len); + + RRB_GET_OPTIONAL(FT_RRB_RADIUS_CUI, radius_cui, msgtype, -1); + if (f_radius_cui) + wpa_hexdump_ascii(MSG_DEBUG, "FT: CUI", f_radius_cui, + f_radius_cui_len); + + RRB_GET_OPTIONAL(FT_RRB_SESSION_TIMEOUT, session_timeout, msgtype, + sizeof(le32)); + if (f_session_timeout) + session_timeout = WPA_GET_LE32(f_session_timeout); + else + session_timeout = 0; + wpa_printf(MSG_DEBUG, "FT: session_timeout %d", session_timeout); + + if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, pmk_r1_len, + f_pmk_r1_name, + pairwise, &vlan, expires_in, session_timeout, + f_identity, f_identity_len, f_radius_cui, + f_radius_cui_len) < 0) + goto out; + + ret = 0; +out: + if (plain) { + os_memset(plain, 0, plain_len); + os_free(plain); + } + + return ret; + +} + + +static void ft_finish_pull(struct wpa_state_machine *sm) { - struct wpa_state_machine *sm = eloop_ctx; int res; u8 *resp_ies; size_t resp_ies_len; u16 status; + if (!sm->ft_pending_cb || !sm->ft_pending_req_ies) + return; + res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies), wpabuf_len(sm->ft_pending_req_ies), &resp_ies, &resp_ies_len); + if (res < 0) { + /* this loop is broken by ft_pending_pull_left_retries */ + wpa_printf(MSG_DEBUG, + "FT: Callback postponed until response is available"); + return; + } wpabuf_free(sm->ft_pending_req_ies); sm->ft_pending_req_ies = NULL; - if (res < 0) - res = WLAN_STATUS_UNSPECIFIED_FAILURE; status = res; wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR " - status %u", MAC2STR(sm->addr), status); @@ -1433,171 +3794,383 @@ static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx) } -static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx) +struct ft_get_sta_ctx { + const u8 *nonce; + const u8 *s1kh_id; + struct wpa_state_machine *sm; +}; + + +static int ft_get_sta_cb(struct wpa_state_machine *sm, void *ctx) { - struct ft_r0kh_r1kh_resp_frame *frame = ctx; + struct ft_get_sta_ctx *info = ctx; - if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0) - return 0; - if (os_memcmp(frame->nonce, sm->ft_pending_pull_nonce, - FT_R0KH_R1KH_PULL_NONCE_LEN) != 0) - return 0; - if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL) + if ((info->s1kh_id && + os_memcmp(info->s1kh_id, sm->addr, ETH_ALEN) != 0) || + os_memcmp(info->nonce, sm->ft_pending_pull_nonce, + FT_RRB_NONCE_LEN) != 0 || + sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL) return 0; - wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for " - MACSTR " - process from timeout", MAC2STR(sm->addr)); - eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL); + info->sm = sm; + return 1; } static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, const u8 *src_addr, - const u8 *data, size_t data_len) + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer) { - struct ft_r0kh_r1kh_resp_frame f; - const u8 *crypt; - u8 *plain; - struct ft_remote_r0kh *r0kh; - int pairwise, res; + const char *msgtype = "pull response"; + int nak, ret = -1; + struct ft_get_sta_ctx ctx; + u8 s1kh_id[ETH_ALEN]; + const u8 *f_nonce; + size_t f_nonce_len; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response"); - if (data_len < sizeof(f)) - return -1; + RRB_GET_AUTH(FT_RRB_NONCE, nonce, msgtype, FT_RRB_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len); - r0kh = wpa_auth->conf.r0kh_list; - while (r0kh) { - if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) - break; - r0kh = r0kh->next; - } - if (r0kh == NULL) { - wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " - "PMK-R0 pull response source address " MACSTR, - MAC2STR(src_addr)); + os_memset(&ctx, 0, sizeof(ctx)); + ctx.nonce = f_nonce; + if (!wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) { + /* nonce not found */ + wpa_printf(MSG_DEBUG, "FT: Invalid nonce"); return -1; } - crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce); - os_memset(&f, 0, sizeof(f)); - plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce); - /* aes_unwrap() does not support inplace decryption, so use a temporary - * buffer for the data. */ - if (aes_unwrap(r0kh->key, sizeof(r0kh->key), - (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, - crypt, plain) < 0) { - wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " - "response from " MACSTR, MAC2STR(src_addr)); - return -1; + ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_RESP, + enc, enc_len, auth, auth_len, msgtype, s1kh_id, + no_defer ? NULL : &wpa_ft_rrb_rx_resp); + if (ret == -2) { + ret = 0; + nak = 1; + } else { + nak = 0; } - - if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder, - FT_R1KH_ID_LEN) != 0) { - wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a " - "matching R1KH-ID"); + if (ret < 0) return -1; - } - - pairwise = le_to_host16(f.pairwise); - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", - f.nonce, sizeof(f.nonce)); - wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID=" - MACSTR " pairwise=0x%x", - MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1", - f.pmk_r1, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name", - f.pmk_r1_name, WPA_PMK_NAME_LEN); - res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, - pairwise); - wpa_printf(MSG_DEBUG, "FT: Look for pending pull request"); - wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f); - os_memset(f.pmk_r1, 0, PMK_LEN); + ctx.s1kh_id = s1kh_id; + if (wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) { + wpa_printf(MSG_DEBUG, + "FT: Response to a pending pull request for " MACSTR, + MAC2STR(ctx.sm->addr)); + eloop_cancel_timeout(wpa_ft_expire_pull, ctx.sm, NULL); + if (nak) + ctx.sm->ft_pending_pull_left_retries = 0; + ft_finish_pull(ctx.sm); + } - return res ? 0 : -1; +out: + return ret; } static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, const u8 *src_addr, - const u8 *data, size_t data_len) + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, int no_defer) { - struct ft_r0kh_r1kh_push_frame f; - const u8 *crypt; - u8 *plain; - struct ft_remote_r0kh *r0kh; - struct os_time now; - os_time_t tsend; - int pairwise; + const char *msgtype = "push"; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push"); - if (data_len < sizeof(f)) + if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_PUSH, + enc, enc_len, auth, auth_len, msgtype, NULL, + no_defer ? NULL : wpa_ft_rrb_rx_push) < 0) return -1; - r0kh = wpa_auth->conf.r0kh_list; - while (r0kh) { - if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) - break; - r0kh = r0kh->next; + return 0; +} + + +static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, int type, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + struct ft_remote_seq **rkh_seq, + u8 **key, size_t *key_len, + struct ft_remote_r0kh **r0kh_out, + struct ft_remote_r1kh **r1kh_out, + struct ft_remote_r0kh **r0kh_wildcard_out, + struct ft_remote_r1kh **r1kh_wildcard_out) +{ + struct ft_remote_r0kh *r0kh = NULL; + struct ft_remote_r1kh *r1kh = NULL; + const u8 *f_r0kh_id, *f_r1kh_id; + size_t f_r0kh_id_len, f_r1kh_id_len; + int to_r0kh, to_r1kh; + u8 *plain = NULL; + size_t plain_len = 0; + struct ft_remote_r0kh *r0kh_wildcard; + struct ft_remote_r1kh *r1kh_wildcard; + + RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1); + RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN); + + to_r0kh = !wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len); + to_r1kh = !wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id); + + if (to_r0kh && to_r1kh) { + wpa_printf(MSG_DEBUG, "FT: seq - local R0KH-ID and R1KH-ID"); + goto out; } - if (r0kh == NULL) { - wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " - "PMK-R0 push source address " MACSTR, - MAC2STR(src_addr)); - return -1; + + if (!to_r0kh && !to_r1kh) { + wpa_printf(MSG_DEBUG, "FT: seq - remote R0KH-ID and R1KH-ID"); + goto out; } - crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp); - os_memset(&f, 0, sizeof(f)); - plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame, - timestamp); - /* aes_unwrap() does not support inplace decryption, so use a temporary - * buffer for the data. */ - if (aes_unwrap(r0kh->key, sizeof(r0kh->key), - (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, - crypt, plain) < 0) { - wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from " - MACSTR, MAC2STR(src_addr)); - return -1; + if (!to_r0kh) { + wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, + &r0kh, &r0kh_wildcard); + if (!r0kh_wildcard && + (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) { + wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID", + f_r0kh_id, f_r0kh_id_len); + goto out; + } + if (r0kh) { + *key = r0kh->key; + *key_len = sizeof(r0kh->key); + } else { + *key = r0kh_wildcard->key; + *key_len = sizeof(r0kh_wildcard->key); + } } - os_get_time(&now); - tsend = WPA_GET_LE32(f.timestamp); - if ((now.sec > tsend && now.sec - tsend > 60) || - (now.sec < tsend && tsend - now.sec > 60)) { - wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid " - "timestamp: sender time %d own time %d\n", - (int) tsend, (int) now.sec); - return -1; + if (!to_r1kh) { + wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, + &r1kh_wildcard); + if (!r1kh_wildcard && + (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) { + wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID", + f_r1kh_id, FT_R1KH_ID_LEN); + goto out; + } + if (r1kh) { + *key = r1kh->key; + *key_len = sizeof(r1kh->key); + } else { + *key = r1kh_wildcard->key; + *key_len = sizeof(r1kh_wildcard->key); + } } - if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder, - FT_R1KH_ID_LEN) != 0) { - wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching " - "R1KH-ID (received " MACSTR " own " MACSTR ")", - MAC2STR(f.r1kh_id), - MAC2STR(wpa_auth->conf.r1_key_holder)); - return -1; + if (wpa_ft_rrb_decrypt(*key, *key_len, enc, enc_len, auth, auth_len, + src_addr, type, &plain, &plain_len) < 0) + goto out; + + os_free(plain); + + if (!to_r0kh) { + if (!r0kh) + r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, + src_addr, f_r0kh_id, + f_r0kh_id_len, + ftRRBseqTimeout); + if (!r0kh) + goto out; + + wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, ftRRBseqTimeout); + *rkh_seq = r0kh->seq; + if (r0kh_out) + *r0kh_out = r0kh; + if (r0kh_wildcard_out) + *r0kh_wildcard_out = r0kh_wildcard; + } + + if (!to_r1kh) { + if (!r1kh) + r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, + src_addr, f_r1kh_id, + ftRRBseqTimeout); + if (!r1kh) + goto out; + + wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, ftRRBseqTimeout); + *rkh_seq = r1kh->seq; + if (r1kh_out) + *r1kh_out = r1kh; + if (r1kh_wildcard_out) + *r1kh_wildcard_out = r1kh_wildcard; + } + + return 0; +out: + return -1; +} + + +static int wpa_ft_rrb_rx_seq_req(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer) +{ + int ret = -1; + struct ft_rrb_seq f_seq; + const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id; + size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len; + struct ft_remote_seq *rkh_seq = NULL; + u8 *packet = NULL, *key = NULL; + size_t packet_len = 0, key_len = 0; + struct tlv_list seq_resp_auth[5]; + + wpa_printf(MSG_DEBUG, "FT: Received sequence number request"); + + if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ, + enc, enc_len, auth, auth_len, &rkh_seq, &key, + &key_len, NULL, NULL, NULL, NULL) < 0) + goto out; + + RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq request", FT_RRB_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: seq request - nonce", f_nonce, f_nonce_len); + + RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1); + RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN); + + if (wpa_ft_new_seq(rkh_seq, &f_seq) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); + goto out; } - pairwise = le_to_host16(f.pairwise); - wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID=" - MACSTR " pairwise=0x%x", - MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1", - f.pmk_r1, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name", - f.pmk_r1_name, WPA_PMK_NAME_LEN); + seq_resp_auth[0].type = FT_RRB_NONCE; + seq_resp_auth[0].len = f_nonce_len; + seq_resp_auth[0].data = f_nonce; + seq_resp_auth[1].type = FT_RRB_SEQ; + seq_resp_auth[1].len = sizeof(f_seq); + seq_resp_auth[1].data = (u8 *) &f_seq; + seq_resp_auth[2].type = FT_RRB_R0KH_ID; + seq_resp_auth[2].len = f_r0kh_id_len; + seq_resp_auth[2].data = f_r0kh_id; + seq_resp_auth[3].type = FT_RRB_R1KH_ID; + seq_resp_auth[3].len = FT_R1KH_ID_LEN; + seq_resp_auth[3].data = f_r1kh_id; + seq_resp_auth[4].type = FT_RRB_LAST_EMPTY; + seq_resp_auth[4].len = 0; + seq_resp_auth[4].data = NULL; - wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, - pairwise); - os_memset(f.pmk_r1, 0, PMK_LEN); + if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth, NULL, + wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_RESP, + &packet, &packet_len) < 0) + goto out; + + wpa_ft_rrb_oui_send(wpa_auth, src_addr, + FT_PACKET_R0KH_R1KH_SEQ_RESP, packet, + packet_len); + +out: + os_free(packet); + + return ret; +} + + +static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer) +{ + u8 *key = NULL; + size_t key_len = 0; + struct ft_remote_r0kh *r0kh = NULL, *r0kh_wildcard = NULL; + struct ft_remote_r1kh *r1kh = NULL, *r1kh_wildcard = NULL; + const u8 *f_nonce, *f_seq; + size_t f_nonce_len, f_seq_len; + struct ft_remote_seq *rkh_seq = NULL; + struct ft_remote_item *item; + struct os_reltime now, now_remote; + int seq_ret, found; + const struct ft_rrb_seq *msg_both; + u32 msg_dom, msg_seq; + + wpa_printf(MSG_DEBUG, "FT: Received sequence number response"); + + if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_RESP, + enc, enc_len, auth, auth_len, &rkh_seq, &key, + &key_len, &r0kh, &r1kh, &r0kh_wildcard, + &r1kh_wildcard) < 0) + goto out; + + RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq response", FT_RRB_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: seq response - nonce", f_nonce, + f_nonce_len); + + found = 0; + dl_list_for_each(item, &rkh_seq->rx.queue, struct ft_remote_item, + list) { + if (os_memcmp_const(f_nonce, item->nonce, + FT_RRB_NONCE_LEN) != 0 || + os_get_reltime(&now) < 0 || + os_reltime_expired(&now, &item->nonce_ts, ftRRBseqTimeout)) + continue; + + found = 1; + break; + } + if (!found) { + wpa_printf(MSG_DEBUG, "FT: seq response - bad nonce"); + goto out; + } + + if (r0kh) { + wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, + wpa_auth->conf.rkh_pos_timeout); + if (r0kh_wildcard) + os_memcpy(r0kh->addr, src_addr, ETH_ALEN); + } + + if (r1kh) { + wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, + wpa_auth->conf.rkh_pos_timeout); + if (r1kh_wildcard) + os_memcpy(r1kh->addr, src_addr, ETH_ALEN); + } + + seq_ret = wpa_ft_rrb_seq_chk(rkh_seq, src_addr, enc, enc_len, auth, + auth_len, "seq response", 1); + if (seq_ret == FT_RRB_SEQ_OK) { + wpa_printf(MSG_DEBUG, "FT: seq response - valid seq number"); + wpa_ft_rrb_seq_accept(wpa_auth, rkh_seq, src_addr, auth, + auth_len, "seq response"); + } else { + wpa_printf(MSG_DEBUG, "FT: seq response - reset seq number"); + + RRB_GET_AUTH(FT_RRB_SEQ, seq, "seq response", + sizeof(*msg_both)); + msg_both = (const struct ft_rrb_seq *) f_seq; + + msg_dom = le_to_host32(msg_both->dom); + msg_seq = le_to_host32(msg_both->seq); + now_remote.sec = le_to_host32(msg_both->ts); + now_remote.usec = 0; + + rkh_seq->rx.num_last = 2; + rkh_seq->rx.dom = msg_dom; + rkh_seq->rx.offsetidx = 0; + /* Accept some older, possibly cached packets as well */ + rkh_seq->rx.last[0] = msg_seq - FT_REMOTE_SEQ_BACKLOG - + dl_list_len(&rkh_seq->rx.queue); + rkh_seq->rx.last[1] = msg_seq; + + /* local time - offset = remote time + * <=> local time - remote time = offset */ + os_reltime_sub(&now, &now_remote, &rkh_seq->rx.time_offset); + } + + wpa_ft_rrb_seq_flush(wpa_auth, rkh_seq, 1); return 0; +out: + return -1; } @@ -1642,13 +4215,6 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, return -1; } - if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL) - return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len); - if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP) - return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len); - if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH) - return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len); - wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen); if (alen < 1 + 1 + 2 * ETH_ALEN) { @@ -1726,65 +4292,137 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, } -static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, - struct wpa_ft_pmk_r0_sa *pmk_r0, - struct ft_remote_r1kh *r1kh, - const u8 *s1kh_id, int pairwise) +void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, const u8 *data, + size_t data_len) { - struct ft_r0kh_r1kh_push_frame frame, f; - struct os_time now; - const u8 *plain; - u8 *crypt; + const u8 *auth, *enc; + size_t alen, elen; + int no_defer = 0; - os_memset(&frame, 0, sizeof(frame)); - frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; - frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH; - frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN); - os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "FT: RRB-OUI received frame from remote AP " + MACSTR, MAC2STR(src_addr)); + wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame - oui_suffix=%d", oui_suffix); - /* aes_wrap() does not support inplace encryption, so use a temporary - * buffer for the data. */ - os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN); - os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); - os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN); - wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id, - s1kh_id, f.pmk_r1, f.pmk_r1_name); - wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id)); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name, - WPA_PMK_NAME_LEN); - os_get_time(&now); - WPA_PUT_LE32(f.timestamp, now.sec); - f.pairwise = host_to_le16(pairwise); - os_memset(f.pad, 0, sizeof(f.pad)); - plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame, - timestamp); - crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame, - timestamp); - if (aes_wrap(r1kh->key, sizeof(r1kh->key), - (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, - plain, crypt) < 0) + if (is_multicast_ether_addr(src_addr)) { + wpa_printf(MSG_DEBUG, + "FT: RRB-OUI received frame from multicast address " + MACSTR, MAC2STR(src_addr)); + return; + } + + if (is_multicast_ether_addr(dst_addr)) { + wpa_printf(MSG_DEBUG, + "FT: RRB-OUI received frame from remote AP " MACSTR + " to multicast address " MACSTR, + MAC2STR(src_addr), MAC2STR(dst_addr)); + no_defer = 1; + } + + if (data_len < sizeof(u16)) { + wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short"); return; + } + + alen = WPA_GET_LE16(data); + if (data_len < sizeof(u16) + alen) { + wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short"); + return; + } + + auth = data + sizeof(u16); + enc = data + sizeof(u16) + alen; + elen = data_len - sizeof(u16) - alen; + + switch (oui_suffix) { + case FT_PACKET_R0KH_R1KH_PULL: + wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen, + no_defer); + break; + case FT_PACKET_R0KH_R1KH_RESP: + wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen, + no_defer); + break; + case FT_PACKET_R0KH_R1KH_PUSH: + wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen, + no_defer); + break; + case FT_PACKET_R0KH_R1KH_SEQ_REQ: + wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen, + no_defer); + break; + case FT_PACKET_R0KH_R1KH_SEQ_RESP: + wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth, + alen, no_defer); + break; + } +} - wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame)); + +static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, + struct wpa_ft_pmk_r0_sa *pmk_r0, + struct ft_remote_r1kh *r1kh, + const u8 *s1kh_id) +{ + u8 *packet; + size_t packet_len; + struct ft_rrb_seq f_seq; + struct tlv_list push[] = { + { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN, + .data = s1kh_id }, + { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN, + .data = pmk_r0->pmk_r0_name }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + struct tlv_list push_auth[] = { + { .type = FT_RRB_SEQ, .len = sizeof(f_seq), + .data = (u8 *) &f_seq }, + { .type = FT_RRB_R0KH_ID, + .len = wpa_auth->conf.r0_key_holder_len, + .data = wpa_auth->conf.r0_key_holder }, + { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN, + .data = r1kh->id }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + + if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); + return -1; + } + + if (wpa_ft_rrb_build_r0(r1kh->key, sizeof(r1kh->key), push, pmk_r0, + r1kh->id, s1kh_id, push_auth, wpa_auth->addr, + FT_PACKET_R0KH_R1KH_PUSH, + &packet, &packet_len) < 0) + return -1; + + wpa_ft_rrb_oui_send(wpa_auth, r1kh->addr, FT_PACKET_R0KH_R1KH_PUSH, + packet, packet_len); + + os_free(packet); + return 0; } void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr) { - struct wpa_ft_pmk_r0_sa *r0; + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r0_sa *r0, *r0found = NULL; struct ft_remote_r1kh *r1kh; if (!wpa_auth->conf.pmk_r1_push) return; + if (!wpa_auth->conf.r1kh_list) + return; - r0 = wpa_auth->ft_pmk_cache->pmk_r0; - while (r0) { - if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) + dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) { + if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) { + r0found = r0; break; - r0 = r0->next; + } } + r0 = r0found; if (r0 == NULL || r0->pmk_r1_pushed) return; r0->pmk_r1_pushed = 1; @@ -1792,11 +4430,14 @@ void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr) wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs " "for STA " MACSTR, MAC2STR(addr)); - r1kh = wpa_auth->conf.r1kh_list; - while (r1kh) { - wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise); - r1kh = r1kh->next; + for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) { + if (is_zero_ether_addr(r1kh->addr) || + is_zero_ether_addr(r1kh->id)) + continue; + if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0) + continue; + wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr); } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 21424147e443..812740301c8b 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -9,6 +9,8 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" +#include "utils/list.h" #include "common/ieee802_11_defs.h" #include "common/sae.h" #include "common/wpa_ctrl.h" @@ -17,6 +19,7 @@ #include "eapol_auth/eapol_auth_sm_i.h" #include "eap_server/eap.h" #include "l2_packet/l2_packet.h" +#include "eth_p_oui.h" #include "hostapd.h" #include "ieee802_1x.h" #include "preauth_auth.h" @@ -24,6 +27,7 @@ #include "tkip_countermeasures.h" #include "ap_drv_ops.h" #include "ap_config.h" +#include "pmksa_cache_auth.h" #include "wpa_auth.h" #include "wpa_auth_glue.h" @@ -41,10 +45,13 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->wpa_strict_rekey = conf->wpa_strict_rekey; wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey; wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey; + wconf->wpa_group_update_count = conf->wpa_group_update_count; + wconf->wpa_disable_eapol_key_retries = + conf->wpa_disable_eapol_key_retries; + wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count; wconf->rsn_pairwise = conf->rsn_pairwise; wconf->rsn_preauth = conf->rsn_preauth; wconf->eapol_version = conf->eapol_version; - wconf->peerkey = conf->peerkey; wconf->wmm_enabled = conf->wmm_enabled; wconf->wmm_uapsd = conf->wmm_uapsd; wconf->disable_pmksa_caching = conf->disable_pmksa_caching; @@ -52,8 +59,9 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, #ifdef CONFIG_IEEE80211W wconf->ieee80211w = conf->ieee80211w; wconf->group_mgmt_cipher = conf->group_mgmt_cipher; + wconf->sae_require_mfp = conf->sae_require_mfp; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP wconf->ssid_len = conf->ssid.ssid_len; if (wconf->ssid_len > SSID_MAX_LEN) wconf->ssid_len = SSID_MAX_LEN; @@ -68,12 +76,18 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, } os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN); wconf->r0_key_lifetime = conf->r0_key_lifetime; + wconf->r1_max_key_lifetime = conf->r1_max_key_lifetime; wconf->reassociation_deadline = conf->reassociation_deadline; - wconf->r0kh_list = conf->r0kh_list; - wconf->r1kh_list = conf->r1kh_list; + wconf->rkh_pos_timeout = conf->rkh_pos_timeout; + wconf->rkh_neg_timeout = conf->rkh_neg_timeout; + wconf->rkh_pull_timeout = conf->rkh_pull_timeout; + wconf->rkh_pull_retries = conf->rkh_pull_retries; + wconf->r0kh_list = &conf->r0kh_list; + wconf->r1kh_list = &conf->r1kh_list; wconf->pmk_r1_push = conf->pmk_r1_push; wconf->ft_over_ds = conf->ft_over_ds; -#endif /* CONFIG_IEEE80211R */ + wconf->ft_psk_generate_local = conf->ft_psk_generate_local; +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_HS20 wconf->disable_gtk = conf->disable_dgaf; if (conf->osen) { @@ -107,6 +121,11 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4); os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4); #endif /* CONFIG_P2P */ +#ifdef CONFIG_FILS + wconf->fils_cache_id_set = conf->fils_cache_id_set; + os_memcpy(wconf->fils_cache_id, conf->fils_cache_id, + FILS_CACHE_ID_LEN); +#endif /* CONFIG_FILS */ } @@ -223,20 +242,47 @@ static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr, static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, - const u8 *prev_psk) + const u8 *prev_psk, size_t *psk_len) { struct hostapd_data *hapd = ctx; struct sta_info *sta = ap_get_sta(hapd, addr); const u8 *psk; + if (psk_len) + *psk_len = PMK_LEN; + #ifdef CONFIG_SAE if (sta && sta->auth_alg == WLAN_AUTH_SAE) { if (!sta->sae || prev_psk) return NULL; return sta->sae->pmk; } + if (sta && wpa_auth_uses_sae(sta->wpa_sm)) { + wpa_printf(MSG_DEBUG, + "No PSK for STA trying to use SAE with PMKSA caching"); + return NULL; + } #endif /* CONFIG_SAE */ +#ifdef CONFIG_OWE + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && + sta && sta->owe_pmk) { + if (psk_len) + *psk_len = sta->owe_pmk_len; + return sta->owe_pmk; + } + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && sta) { + struct rsn_pmksa_cache_entry *sa; + + sa = wpa_auth_sta_get_pmksa(sta->wpa_sm); + if (sa && sa->akmp == WPA_KEY_MGMT_OWE) { + if (psk_len) + *psk_len = sa->pmk_len; + return sa->pmk; + } + } +#endif /* CONFIG_OWE */ + psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk); /* * This is about to iterate over all psks, prev_psk gives the last @@ -307,6 +353,37 @@ static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, return -1; } +#ifdef CONFIG_TESTING_OPTIONS + if (addr && !is_broadcast_ether_addr(addr)) { + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (sta) { + sta->last_tk_alg = alg; + sta->last_tk_key_idx = idx; + if (key) + os_memcpy(sta->last_tk, key, key_len); + sta->last_tk_len = key_len; + } +#ifdef CONFIG_IEEE80211W + } else if (alg == WPA_ALG_IGTK || + alg == WPA_ALG_BIP_GMAC_128 || + alg == WPA_ALG_BIP_GMAC_256 || + alg == WPA_ALG_BIP_CMAC_256) { + hapd->last_igtk_alg = alg; + hapd->last_igtk_key_idx = idx; + if (key) + os_memcpy(hapd->last_igtk, key, key_len); + hapd->last_igtk_len = key_len; +#endif /* CONFIG_IEEE80211W */ + } else { + hapd->last_gtk_alg = alg; + hapd->last_gtk_key_idx = idx; + if (key) + os_memcpy(hapd->last_gtk, key, key_len); + hapd->last_gtk_len = key_len; + } +#endif /* CONFIG_TESTING_OPTIONS */ return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0, key, key_len); } @@ -401,7 +478,32 @@ static int hostapd_wpa_auth_for_each_auth( } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP + +struct wpa_ft_rrb_rx_later_data { + struct dl_list list; + u8 addr[ETH_ALEN]; + size_t data_len; + /* followed by data_len octets of data */ +}; + +static void hostapd_wpa_ft_rrb_rx_later(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct wpa_ft_rrb_rx_later_data *data, *n; + + dl_list_for_each_safe(data, n, &hapd->l2_queue, + struct wpa_ft_rrb_rx_later_data, list) { + if (hapd->wpa_auth) { + wpa_ft_rrb_rx(hapd->wpa_auth, data->addr, + (const u8 *) (data + 1), + data->data_len); + } + dl_list_del(&data->list); + os_free(data); + } +} + struct wpa_auth_ft_iface_iter_data { struct hostapd_data *src_hapd; @@ -414,33 +516,54 @@ struct wpa_auth_ft_iface_iter_data { static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx) { struct wpa_auth_ft_iface_iter_data *idata = ctx; + struct wpa_ft_rrb_rx_later_data *data; struct hostapd_data *hapd; size_t j; for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; - if (hapd == idata->src_hapd) + if (hapd == idata->src_hapd || + !hapd->wpa_auth || + os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) != 0) continue; - if (!hapd->wpa_auth) - continue; - if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) { - wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to " - "locally managed BSS " MACSTR "@%s -> " - MACSTR "@%s", - MAC2STR(idata->src_hapd->own_addr), - idata->src_hapd->conf->iface, - MAC2STR(hapd->own_addr), hapd->conf->iface); - wpa_ft_rrb_rx(hapd->wpa_auth, - idata->src_hapd->own_addr, - idata->data, idata->data_len); + + wpa_printf(MSG_DEBUG, + "FT: Send RRB data directly to locally managed BSS " + MACSTR "@%s -> " MACSTR "@%s", + MAC2STR(idata->src_hapd->own_addr), + idata->src_hapd->conf->iface, + MAC2STR(hapd->own_addr), hapd->conf->iface); + + /* Defer wpa_ft_rrb_rx() until next eloop step as this is + * when it would be triggered when reading from a socket. + * This avoids + * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv, + * that is calling hapd0:recv handler from within + * hapd0:send directly. + */ + data = os_zalloc(sizeof(*data) + idata->data_len); + if (!data) return 1; - } + + os_memcpy(data->addr, idata->src_hapd->own_addr, ETH_ALEN); + os_memcpy(data + 1, idata->data, idata->data_len); + data->data_len = idata->data_len; + + dl_list_add(&hapd->l2_queue, &data->list); + + if (!eloop_is_timeout_registered(hostapd_wpa_ft_rrb_rx_later, + hapd, NULL)) + eloop_register_timeout(0, 0, + hostapd_wpa_ft_rrb_rx_later, + hapd, NULL); + + return 1; } return 0; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, @@ -465,7 +588,7 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, } #endif /* CONFIG_TESTING_OPTIONS */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (proto == ETH_P_RRB && hapd->iface->interfaces && hapd->iface->interfaces->for_each_interface) { int res; @@ -480,7 +603,7 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, if (res == 1) return data_len; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (hapd->driver && hapd->driver->send_ether) return hapd->driver->send_ether(hapd->drv_priv, dst, @@ -503,7 +626,157 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_ETH_P_OUI +static struct eth_p_oui_ctx * hostapd_wpa_get_oui(struct hostapd_data *hapd, + u8 oui_suffix) +{ + switch (oui_suffix) { +#ifdef CONFIG_IEEE80211R_AP + case FT_PACKET_R0KH_R1KH_PULL: + return hapd->oui_pull; + case FT_PACKET_R0KH_R1KH_RESP: + return hapd->oui_resp; + case FT_PACKET_R0KH_R1KH_PUSH: + return hapd->oui_push; + case FT_PACKET_R0KH_R1KH_SEQ_REQ: + return hapd->oui_sreq; + case FT_PACKET_R0KH_R1KH_SEQ_RESP: + return hapd->oui_sresp; +#endif /* CONFIG_IEEE80211R_AP */ + default: + return NULL; + } +} +#endif /* CONFIG_ETH_P_OUI */ + + +#ifdef CONFIG_IEEE80211R_AP + +struct oui_deliver_later_data { + struct dl_list list; + u8 src_addr[ETH_ALEN]; + u8 dst_addr[ETH_ALEN]; + size_t data_len; + u8 oui_suffix; + /* followed by data_len octets of data */ +}; + +static void hostapd_oui_deliver_later(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct oui_deliver_later_data *data, *n; + struct eth_p_oui_ctx *oui_ctx; + + dl_list_for_each_safe(data, n, &hapd->l2_oui_queue, + struct oui_deliver_later_data, list) { + oui_ctx = hostapd_wpa_get_oui(hapd, data->oui_suffix); + if (hapd->wpa_auth && oui_ctx) { + eth_p_oui_deliver(oui_ctx, data->src_addr, + data->dst_addr, + (const u8 *) (data + 1), + data->data_len); + } + dl_list_del(&data->list); + os_free(data); + } +} + + +struct wpa_auth_oui_iface_iter_data { + struct hostapd_data *src_hapd; + const u8 *dst_addr; + const u8 *data; + size_t data_len; + u8 oui_suffix; +}; + +static int hostapd_wpa_auth_oui_iter(struct hostapd_iface *iface, void *ctx) +{ + struct wpa_auth_oui_iface_iter_data *idata = ctx; + struct oui_deliver_later_data *data; + struct hostapd_data *hapd; + size_t j; + + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + if (hapd == idata->src_hapd) + continue; + if (!is_multicast_ether_addr(idata->dst_addr) && + os_memcmp(hapd->own_addr, idata->dst_addr, ETH_ALEN) != 0) + continue; + + /* defer eth_p_oui_deliver until next eloop step as this is + * when it would be triggerd from reading from sock + * This avoids + * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv, + * that is calling hapd0:recv handler from within + * hapd0:send directly. + */ + data = os_zalloc(sizeof(*data) + idata->data_len); + if (!data) + return 1; + + os_memcpy(data->src_addr, idata->src_hapd->own_addr, ETH_ALEN); + os_memcpy(data->dst_addr, idata->dst_addr, ETH_ALEN); + os_memcpy(data + 1, idata->data, idata->data_len); + data->data_len = idata->data_len; + data->oui_suffix = idata->oui_suffix; + + dl_list_add(&hapd->l2_oui_queue, &data->list); + + if (!eloop_is_timeout_registered(hostapd_oui_deliver_later, + hapd, NULL)) + eloop_register_timeout(0, 0, + hostapd_oui_deliver_later, + hapd, NULL); + + return 1; + } + + return 0; +} + +#endif /* CONFIG_IEEE80211R_AP */ + + +static int hostapd_wpa_auth_send_oui(void *ctx, const u8 *dst, u8 oui_suffix, + const u8 *data, size_t data_len) +{ +#ifdef CONFIG_ETH_P_OUI + struct hostapd_data *hapd = ctx; + struct eth_p_oui_ctx *oui_ctx; + +#ifdef CONFIG_IEEE80211R_AP + if (hapd->iface->interfaces && + hapd->iface->interfaces->for_each_interface) { + struct wpa_auth_oui_iface_iter_data idata; + int res; + + idata.src_hapd = hapd; + idata.dst_addr = dst; + idata.data = data; + idata.data_len = data_len; + idata.oui_suffix = oui_suffix; + res = hapd->iface->interfaces->for_each_interface( + hapd->iface->interfaces, hostapd_wpa_auth_oui_iter, + &idata); + if (res == 1) + return data_len; + } +#endif /* CONFIG_IEEE80211R_AP */ + + oui_ctx = hostapd_wpa_get_oui(hapd, oui_suffix); + if (!oui_ctx) + return -1; + + return eth_p_oui_send(oui_ctx, hapd->own_addr, dst, data, data_len); +#else /* CONFIG_ETH_P_OUI */ + return -1; +#endif /* CONFIG_ETH_P_OUI */ +} + + +#ifdef CONFIG_IEEE80211R_AP static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst, const u8 *data, size_t data_len) @@ -563,6 +836,244 @@ hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr) } +static int hostapd_wpa_auth_set_vlan(void *ctx, const u8 *sta_addr, + struct vlan_description *vlan) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta || !sta->wpa_sm) + return -1; + + if (vlan->notempty && + !hostapd_vlan_valid(hapd->conf->vlan, vlan)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Invalid VLAN %d%s received from FT", + vlan->untagged, vlan->tagged[0] ? "+" : ""); + return -1; + } + + if (ap_sta_set_vlan(hapd, sta, vlan) < 0) + return -1; + /* Configure wpa_group for GTK but ignore error due to driver not + * knowing this STA. */ + ap_sta_bind_vlan(hapd, sta); + + if (sta->vlan_id) + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); + + return 0; +} + + +static int hostapd_wpa_auth_get_vlan(void *ctx, const u8 *sta_addr, + struct vlan_description *vlan) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta) + return -1; + + if (sta->vlan_desc) + *vlan = *sta->vlan_desc; + else + os_memset(vlan, 0, sizeof(*vlan)); + + return 0; +} + + +static int +hostapd_wpa_auth_set_identity(void *ctx, const u8 *sta_addr, + const u8 *identity, size_t identity_len) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta) + return -1; + + os_free(sta->identity); + sta->identity = NULL; + + if (sta->eapol_sm) { + os_free(sta->eapol_sm->identity); + sta->eapol_sm->identity = NULL; + sta->eapol_sm->identity_len = 0; + } + + if (!identity_len) + return 0; + + /* sta->identity is NULL terminated */ + sta->identity = os_zalloc(identity_len + 1); + if (!sta->identity) + return -1; + os_memcpy(sta->identity, identity, identity_len); + + if (sta->eapol_sm) { + sta->eapol_sm->identity = os_zalloc(identity_len); + if (!sta->eapol_sm->identity) + return -1; + os_memcpy(sta->eapol_sm->identity, identity, identity_len); + sta->eapol_sm->identity_len = identity_len; + } + + return 0; +} + + +static size_t +hostapd_wpa_auth_get_identity(void *ctx, const u8 *sta_addr, const u8 **buf) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + size_t len; + char *identity; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta) + return 0; + + *buf = ieee802_1x_get_identity(sta->eapol_sm, &len); + if (*buf && len) + return len; + + if (!sta->identity) { + *buf = NULL; + return 0; + } + + identity = sta->identity; + len = os_strlen(identity); + *buf = (u8 *) identity; + + return len; +} + + +static int +hostapd_wpa_auth_set_radius_cui(void *ctx, const u8 *sta_addr, + const u8 *radius_cui, size_t radius_cui_len) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta) + return -1; + + os_free(sta->radius_cui); + sta->radius_cui = NULL; + + if (sta->eapol_sm) { + wpabuf_free(sta->eapol_sm->radius_cui); + sta->eapol_sm->radius_cui = NULL; + } + + if (!radius_cui) + return 0; + + /* sta->radius_cui is NULL terminated */ + sta->radius_cui = os_zalloc(radius_cui_len + 1); + if (!sta->radius_cui) + return -1; + os_memcpy(sta->radius_cui, radius_cui, radius_cui_len); + + if (sta->eapol_sm) { + sta->eapol_sm->radius_cui = wpabuf_alloc_copy(radius_cui, + radius_cui_len); + if (!sta->eapol_sm->radius_cui) + return -1; + } + + return 0; +} + + +static size_t +hostapd_wpa_auth_get_radius_cui(void *ctx, const u8 *sta_addr, const u8 **buf) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + struct wpabuf *b; + size_t len; + char *radius_cui; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta) + return 0; + + b = ieee802_1x_get_radius_cui(sta->eapol_sm); + if (b) { + len = wpabuf_len(b); + *buf = wpabuf_head(b); + return len; + } + + if (!sta->radius_cui) { + *buf = NULL; + return 0; + } + + radius_cui = sta->radius_cui; + len = os_strlen(radius_cui); + *buf = (u8 *) radius_cui; + + return len; +} + + +static void hostapd_wpa_auth_set_session_timeout(void *ctx, const u8 *sta_addr, + int session_timeout) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta) + return; + + if (session_timeout) { + os_get_reltime(&sta->session_timeout); + sta->session_timeout.sec += session_timeout; + sta->session_timeout_set = 1; + ap_sta_session_timeout(hapd, sta, session_timeout); + } else { + sta->session_timeout_set = 0; + ap_sta_no_session_timeout(hapd, sta); + } +} + + +static int hostapd_wpa_auth_get_session_timeout(void *ctx, const u8 *sta_addr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + struct os_reltime now, remaining; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta || !sta->session_timeout_set) + return 0; + + os_get_reltime(&now); + if (os_reltime_before(&sta->session_timeout, &now)) { + /* already expired, return >0 as timeout was set */ + return 1; + } + + os_reltime_sub(&sta->session_timeout, &now, &remaining); + + return (remaining.sec > 0) ? remaining.sec : 1; +} + + static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { @@ -581,6 +1092,22 @@ static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, } +static void hostapd_rrb_oui_receive(void *ctx, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, + const u8 *buf, size_t len) +{ + struct hostapd_data *hapd = ctx; + + wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> " + MACSTR, MAC2STR(src_addr), MAC2STR(dst_addr)); + if (!is_multicast_ether_addr(dst_addr) && + os_memcmp(hapd->own_addr, dst_addr, ETH_ALEN) != 0) + return; + wpa_ft_rrb_oui_rx(hapd->wpa_auth, src_addr, dst_addr, oui_suffix, buf, + len); +} + + static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr, u8 *tspec_ie, size_t tspec_ielen) { @@ -588,13 +1115,94 @@ static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr, return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen); } -#endif /* CONFIG_IEEE80211R */ + + +static int hostapd_wpa_register_ft_oui(struct hostapd_data *hapd, + const char *ft_iface) +{ + hapd->oui_pull = eth_p_oui_register(hapd, ft_iface, + FT_PACKET_R0KH_R1KH_PULL, + hostapd_rrb_oui_receive, hapd); + if (!hapd->oui_pull) + return -1; + + hapd->oui_resp = eth_p_oui_register(hapd, ft_iface, + FT_PACKET_R0KH_R1KH_RESP, + hostapd_rrb_oui_receive, hapd); + if (!hapd->oui_resp) + return -1; + + hapd->oui_push = eth_p_oui_register(hapd, ft_iface, + FT_PACKET_R0KH_R1KH_PUSH, + hostapd_rrb_oui_receive, hapd); + if (!hapd->oui_push) + return -1; + + hapd->oui_sreq = eth_p_oui_register(hapd, ft_iface, + FT_PACKET_R0KH_R1KH_SEQ_REQ, + hostapd_rrb_oui_receive, hapd); + if (!hapd->oui_sreq) + return -1; + + hapd->oui_sresp = eth_p_oui_register(hapd, ft_iface, + FT_PACKET_R0KH_R1KH_SEQ_RESP, + hostapd_rrb_oui_receive, hapd); + if (!hapd->oui_sresp) + return -1; + + return 0; +} + + +static void hostapd_wpa_unregister_ft_oui(struct hostapd_data *hapd) +{ + eth_p_oui_unregister(hapd->oui_pull); + hapd->oui_pull = NULL; + eth_p_oui_unregister(hapd->oui_resp); + hapd->oui_resp = NULL; + eth_p_oui_unregister(hapd->oui_push); + hapd->oui_push = NULL; + eth_p_oui_unregister(hapd->oui_sreq); + hapd->oui_sreq = NULL; + eth_p_oui_unregister(hapd->oui_sresp); + hapd->oui_sresp = NULL; +} +#endif /* CONFIG_IEEE80211R_AP */ int hostapd_setup_wpa(struct hostapd_data *hapd) { struct wpa_auth_config _conf; - struct wpa_auth_callbacks cb; + static const struct wpa_auth_callbacks cb = { + .logger = hostapd_wpa_auth_logger, + .disconnect = hostapd_wpa_auth_disconnect, + .mic_failure_report = hostapd_wpa_auth_mic_failure_report, + .psk_failure_report = hostapd_wpa_auth_psk_failure_report, + .set_eapol = hostapd_wpa_auth_set_eapol, + .get_eapol = hostapd_wpa_auth_get_eapol, + .get_psk = hostapd_wpa_auth_get_psk, + .get_msk = hostapd_wpa_auth_get_msk, + .set_key = hostapd_wpa_auth_set_key, + .get_seqnum = hostapd_wpa_auth_get_seqnum, + .send_eapol = hostapd_wpa_auth_send_eapol, + .for_each_sta = hostapd_wpa_auth_for_each_sta, + .for_each_auth = hostapd_wpa_auth_for_each_auth, + .send_ether = hostapd_wpa_auth_send_ether, + .send_oui = hostapd_wpa_auth_send_oui, +#ifdef CONFIG_IEEE80211R_AP + .send_ft_action = hostapd_wpa_auth_send_ft_action, + .add_sta = hostapd_wpa_auth_add_sta, + .add_tspec = hostapd_wpa_auth_add_tspec, + .set_vlan = hostapd_wpa_auth_set_vlan, + .get_vlan = hostapd_wpa_auth_get_vlan, + .set_identity = hostapd_wpa_auth_set_identity, + .get_identity = hostapd_wpa_auth_get_identity, + .set_radius_cui = hostapd_wpa_auth_set_radius_cui, + .get_radius_cui = hostapd_wpa_auth_get_radius_cui, + .set_session_timeout = hostapd_wpa_auth_set_session_timeout, + .get_session_timeout = hostapd_wpa_auth_get_session_timeout, +#endif /* CONFIG_IEEE80211R_AP */ + }; const u8 *wpa_ie; size_t wpa_ie_len; @@ -603,28 +1211,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) _conf.tx_status = 1; if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME) _conf.ap_mlme = 1; - os_memset(&cb, 0, sizeof(cb)); - cb.ctx = hapd; - cb.logger = hostapd_wpa_auth_logger; - cb.disconnect = hostapd_wpa_auth_disconnect; - cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report; - cb.psk_failure_report = hostapd_wpa_auth_psk_failure_report; - cb.set_eapol = hostapd_wpa_auth_set_eapol; - cb.get_eapol = hostapd_wpa_auth_get_eapol; - cb.get_psk = hostapd_wpa_auth_get_psk; - cb.get_msk = hostapd_wpa_auth_get_msk; - cb.set_key = hostapd_wpa_auth_set_key; - cb.get_seqnum = hostapd_wpa_auth_get_seqnum; - cb.send_eapol = hostapd_wpa_auth_send_eapol; - cb.for_each_sta = hostapd_wpa_auth_for_each_sta; - cb.for_each_auth = hostapd_wpa_auth_for_each_auth; - cb.send_ether = hostapd_wpa_auth_send_ether; -#ifdef CONFIG_IEEE80211R - cb.send_ft_action = hostapd_wpa_auth_send_ft_action; - cb.add_sta = hostapd_wpa_auth_add_sta; - cb.add_tspec = hostapd_wpa_auth_add_tspec; -#endif /* CONFIG_IEEE80211R */ - hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb); + hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd); if (hapd->wpa_auth == NULL) { wpa_printf(MSG_ERROR, "WPA initialization failed."); return -1; @@ -649,12 +1236,14 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) return -1; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (!hostapd_drv_none(hapd) && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) { - hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ? - hapd->conf->bridge : - hapd->conf->iface, NULL, ETH_P_RRB, + const char *ft_iface; + + ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge : + hapd->conf->iface; + hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB, hostapd_rrb_receive, hapd, 1); if (hapd->l2 == NULL && (hapd->driver == NULL || @@ -663,8 +1252,14 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) "interface"); return -1; } + + if (hostapd_wpa_register_ft_oui(hapd, ft_iface)) { + wpa_printf(MSG_ERROR, + "Failed to open ETH_P_OUI interface"); + return -1; + } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ return 0; @@ -702,8 +1297,13 @@ void hostapd_deinit_wpa(struct hostapd_data *hapd) } ieee802_1x_deinit(hapd); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP + eloop_cancel_timeout(hostapd_wpa_ft_rrb_rx_later, hapd, ELOOP_ALL_CTX); + hostapd_wpa_ft_rrb_rx_later(hapd, NULL); /* flush without delivering */ + eloop_cancel_timeout(hostapd_oui_deliver_later, hapd, ELOOP_ALL_CTX); + hostapd_oui_deliver_later(hapd, NULL); /* flush without delivering */ l2_packet_deinit(hapd->l2); hapd->l2 = NULL; -#endif /* CONFIG_IEEE80211R */ + hostapd_wpa_unregister_ft_oui(hapd); +#endif /* CONFIG_IEEE80211R_AP */ } diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index 7fd8f05fa8ff..b1cea1b49b14 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -9,18 +9,13 @@ #ifndef WPA_AUTH_I_H #define WPA_AUTH_I_H +#include "utils/list.h" + /* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */ #define RSNA_MAX_EAPOL_RETRIES 4 struct wpa_group; -struct wpa_stsl_negotiation { - struct wpa_stsl_negotiation *next; - u8 initiator[ETH_ALEN]; - u8 peer[ETH_ALEN]; -}; - - struct wpa_state_machine { struct wpa_authenticator *wpa_auth; struct wpa_group *group; @@ -48,8 +43,9 @@ struct wpa_state_machine { Boolean AuthenticationRequest; Boolean ReAuthenticationRequest; Boolean Disconnect; - int TimeoutCtr; - int GTimeoutCtr; + u16 disconnect_reason; /* specific reason code to use with Disconnect */ + u32 TimeoutCtr; + u32 GTimeoutCtr; Boolean TimeoutEvt; Boolean EAPOLKeyReceived; Boolean EAPOLKeyPairwise; @@ -62,6 +58,7 @@ struct wpa_state_machine { u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN]; u8 PMK[PMK_LEN_MAX]; unsigned int pmk_len; + u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */ struct wpa_ptk PTK; Boolean PTK_valid; Boolean pairwise_set; @@ -89,11 +86,12 @@ struct wpa_state_machine { unsigned int rx_eapol_key_secure:1; unsigned int update_snonce:1; unsigned int alt_snonce_valid:1; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP unsigned int ft_completed:1; unsigned int pmk_r1_name_valid:1; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ unsigned int is_wnmsleep:1; + unsigned int pmkid_set:1; u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; int req_replay_counter_used; @@ -113,8 +111,9 @@ struct wpa_state_machine { u32 dot11RSNAStatsTKIPLocalMICFailures; u32 dot11RSNAStatsTKIPRemoteMICFailures; -#ifdef CONFIG_IEEE80211R - u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ +#ifdef CONFIG_IEEE80211R_AP + u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the + * first 384 bits of MSK */ size_t xxkey_len; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth * Request */ @@ -129,16 +128,30 @@ struct wpa_state_machine { const u8 *ies, size_t ies_len); void *ft_pending_cb_ctx; struct wpabuf *ft_pending_req_ies; - u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; + u8 ft_pending_pull_nonce[FT_RRB_NONCE_LEN]; u8 ft_pending_auth_transaction; u8 ft_pending_current_ap[ETH_ALEN]; -#endif /* CONFIG_IEEE80211R */ + int ft_pending_pull_left_retries; +#endif /* CONFIG_IEEE80211R_AP */ int pending_1_of_4_timeout; #ifdef CONFIG_P2P u8 ip_addr[4]; #endif /* CONFIG_P2P */ + +#ifdef CONFIG_FILS + u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN]; + u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN]; + size_t fils_key_auth_len; + unsigned int fils_completed:1; +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_TESTING_OPTIONS + void (*eapol_status_cb)(void *ctx1, void *ctx2); + void *eapol_status_cb_ctx1; + void *eapol_status_cb_ctx2; +#endif /* CONFIG_TESTING_OPTIONS */ }; @@ -194,10 +207,9 @@ struct wpa_authenticator { unsigned int dot11RSNATKIPCounterMeasuresInvoked; unsigned int dot11RSNA4WayHandshakeFailures; - struct wpa_stsl_negotiation *stsl_negotiations; - struct wpa_auth_config conf; - struct wpa_auth_callbacks cb; + const struct wpa_auth_callbacks *cb; + void *cb_ctx; u8 *wpa_ie; size_t wpa_ie_len; @@ -213,6 +225,38 @@ struct wpa_authenticator { }; +#ifdef CONFIG_IEEE80211R_AP + +#define FT_REMOTE_SEQ_BACKLOG 16 +struct ft_remote_seq_rx { + u32 dom; + struct os_reltime time_offset; /* local time - offset = remote time */ + + /* accepted sequence numbers: (offset ... offset + 0x40000000] + * (except those in last) + * dropped sequence numbers: (offset - 0x40000000 ... offset] + * all others trigger SEQ_REQ message (except first message) + */ + u32 last[FT_REMOTE_SEQ_BACKLOG]; + unsigned int num_last; + u32 offsetidx; + + struct dl_list queue; /* send nonces + rrb msgs awaiting seq resp */ +}; + +struct ft_remote_seq_tx { + u32 dom; /* non zero if initialized */ + u32 seq; +}; + +struct ft_remote_seq { + struct ft_remote_seq_rx rx; + struct ft_remote_seq_tx tx; +}; + +#endif /* CONFIG_IEEE80211R_AP */ + + int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, const u8 *pmkid); void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, @@ -231,24 +275,10 @@ int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, int (*cb)(struct wpa_authenticator *a, void *ctx), void *cb_ctx); -#ifdef CONFIG_PEERKEY -int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, - struct wpa_stsl_negotiation *neg); -void wpa_smk_error(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - const u8 *key_data, size_t key_data_len); -void wpa_smk_m1(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key, - const u8 *key_data, size_t key_data_len); -void wpa_smk_m3(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key, - const u8 *key_data, size_t key_data_len); -#endif /* CONFIG_PEERKEY */ - -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len); -int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, - size_t r0kh_id_len, +int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384, + const u8 *r0kh_id, size_t r0kh_id_len, const u8 *anonce, const u8 *snonce, u8 *buf, size_t len, const u8 *subelem, size_t subelem_len); @@ -257,6 +287,8 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void); void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache); void wpa_ft_install_ptk(struct wpa_state_machine *sm); -#endif /* CONFIG_IEEE80211R */ +int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, const u8 *pmk_r0, + const u8 *pmk_r0_name); +#endif /* CONFIG_IEEE80211R_AP */ #endif /* WPA_AUTH_I_H */ diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index f79783b91929..cdcc5de39d45 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -1,6 +1,6 @@ /* * hostapd - WPA/RSN IE and KDE definitions - * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -164,18 +164,25 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, pos += RSN_SELECTOR_LEN; num_suites++; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); pos += RSN_SELECTOR_LEN; num_suites++; } +#ifdef CONFIG_SHA384 + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_SHA384 */ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); pos += RSN_SELECTOR_LEN; num_suites++; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); @@ -210,6 +217,51 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, pos += RSN_SELECTOR_LEN; num_suites++; } +#ifdef CONFIG_FILS + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#ifdef CONFIG_IEEE80211R_AP + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_IEEE80211R_AP */ +#endif /* CONFIG_FILS */ +#ifdef CONFIG_OWE + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_DPP */ +#ifdef CONFIG_HS20 + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OSEN) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_HS20 */ #ifdef CONFIG_RSN_TESTING if (rsn_testing) { @@ -230,8 +282,6 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, capab = 0; if (conf->rsn_preauth) capab |= WPA_CAPABILITY_PREAUTH; - if (conf->peerkey) - capab |= WPA_CAPABILITY_PEERKEY_ENABLED; if (conf->wmm_enabled) { /* 4 PTKSA replay counters when using WMM */ capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); @@ -407,7 +457,7 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) return res; pos += res; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) { res = wpa_write_mdie(&wpa_auth->conf, pos, buf + sizeof(buf) - pos); @@ -415,7 +465,7 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) return res; pos += res; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (wpa_auth->conf.wpa & WPA_PROTO_WPA) { res = wpa_write_wpa_ie(&wpa_auth->conf, pos, buf + sizeof(buf) - pos); @@ -474,7 +524,8 @@ static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx) 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) + const u8 *mdie, size_t mdie_len, + const u8 *owe_dh, size_t owe_dh_len) { struct wpa_ie_data data; int ciphers, key_mgmt, res, version; @@ -509,12 +560,28 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_FILS +#ifdef CONFIG_IEEE80211R_AP + else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) + selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384; + else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) + selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256; +#endif /* CONFIG_IEEE80211R_AP */ + else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA384) + selector = RSN_AUTH_KEY_MGMT_FILS_SHA384; + else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA256) + selector = RSN_AUTH_KEY_MGMT_FILS_SHA256; +#endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R_AP +#ifdef CONFIG_SHA384 + else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) + selector = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384; +#endif /* CONFIG_SHA384 */ else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) selector = RSN_AUTH_KEY_MGMT_FT_802_1X; else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) selector = RSN_AUTH_KEY_MGMT_FT_PSK; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256; @@ -531,6 +598,18 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; else if (data.key_mgmt & WPA_KEY_MGMT_PSK) selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; +#ifdef CONFIG_OWE + else if (data.key_mgmt & WPA_KEY_MGMT_OWE) + selector = RSN_AUTH_KEY_MGMT_OWE; +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + else if (data.key_mgmt & WPA_KEY_MGMT_DPP) + selector = RSN_AUTH_KEY_MGMT_DPP; +#endif /* CONFIG_DPP */ +#ifdef CONFIG_HS20 + else if (data.key_mgmt & WPA_KEY_MGMT_OSEN) + selector = RSN_AUTH_KEY_MGMT_OSEN; +#endif /* CONFIG_HS20 */ wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector; selector = wpa_cipher_to_suite(WPA_PROTO_RSN, @@ -591,12 +670,28 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_FILS +#ifdef CONFIG_IEEE80211R_AP + else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384; + else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256; +#endif /* CONFIG_IEEE80211R_AP */ + else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA384; + else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA256; +#endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R_AP +#ifdef CONFIG_SHA384 + else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; +#endif /* CONFIG_SHA384 */ else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; else if (key_mgmt & WPA_KEY_MGMT_FT_PSK) sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; @@ -611,6 +706,18 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, #endif /* CONFIG_SAE */ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; +#ifdef CONFIG_OWE + else if (key_mgmt & WPA_KEY_MGMT_OWE) + sm->wpa_key_mgmt = WPA_KEY_MGMT_OWE; +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + else if (key_mgmt & WPA_KEY_MGMT_DPP) + sm->wpa_key_mgmt = WPA_KEY_MGMT_DPP; +#endif /* CONFIG_DPP */ +#ifdef CONFIG_HS20 + else if (key_mgmt & WPA_KEY_MGMT_OSEN) + sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN; +#endif /* CONFIG_HS20 */ else sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK; @@ -634,12 +741,6 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, return WPA_MGMT_FRAME_PROTECTION_VIOLATION; } - if (ciphers & WPA_CIPHER_TKIP) { - wpa_printf(MSG_DEBUG, "Management frame protection " - "cannot use TKIP"); - return WPA_MGMT_FRAME_PROTECTION_VIOLATION; - } - if (data.mgmt_group_cipher != wpa_auth->conf.group_mgmt_cipher) { wpa_printf(MSG_DEBUG, "Unsupported management group " @@ -648,14 +749,31 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, } } +#ifdef CONFIG_SAE + if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_OPTIONAL && + wpa_auth->conf.sae_require_mfp && + wpa_key_mgmt_sae(sm->wpa_key_mgmt) && + !(data.capabilities & WPA_CAPABILITY_MFPC)) { + wpa_printf(MSG_DEBUG, + "Management frame protection required with SAE, but client did not enable it"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } +#endif /* CONFIG_SAE */ + if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION || !(data.capabilities & WPA_CAPABILITY_MFPC)) sm->mgmt_frame_prot = 0; else sm->mgmt_frame_prot = 1; + + if (sm->mgmt_frame_prot && (ciphers & WPA_CIPHER_TKIP)) { + wpa_printf(MSG_DEBUG, + "Management frame protection cannot use TKIP"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) { wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but " @@ -668,8 +786,25 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, "MDIE", mdie, MOBILITY_DOMAIN_ID_LEN); return WPA_INVALID_MDIE; } + } else if (mdie != NULL) { + wpa_printf(MSG_DEBUG, + "RSN: Trying to use non-FT AKM suite, but MDIE included"); + return WPA_INVALID_AKMP; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ + +#ifdef CONFIG_OWE + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && !owe_dh) { + wpa_printf(MSG_DEBUG, + "OWE: No Diffie-Hellman Parameter element"); + return WPA_INVALID_AKMP; + } + if (sm->wpa_key_mgmt != WPA_KEY_MGMT_OWE && owe_dh) { + wpa_printf(MSG_DEBUG, + "OWE: Unexpected Diffie-Hellman Parameter element with non-OWE AKM"); + return WPA_INVALID_AKMP; + } +#endif /* CONFIG_OWE */ sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0); if (sm->pairwise < 0) @@ -723,6 +858,23 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN); } +#ifdef CONFIG_SAE + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE && data.num_pmkid && + !sm->pmksa) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "No PMKSA cache entry found for SAE"); + return WPA_INVALID_PMKID; + } +#endif /* CONFIG_SAE */ + +#ifdef CONFIG_DPP + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && !sm->pmksa) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "No PMKSA cache entry found for DPP"); + return WPA_INVALID_PMKID; + } +#endif /* CONFIG_DPP */ + if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) { os_free(sm->wpa_ie); sm->wpa_ie = os_malloc(wpa_ie_len); @@ -815,36 +967,6 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, return 0; } -#ifdef CONFIG_PEERKEY - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) { - ie->smk = pos + 2 + RSN_SELECTOR_LEN; - ie->smk_len = pos[1] - RSN_SELECTOR_LEN; - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) { - ie->nonce = pos + 2 + RSN_SELECTOR_LEN; - ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) { - ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; - ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) { - ie->error = pos + 2 + RSN_SELECTOR_LEN; - ie->error_len = pos[1] - RSN_SELECTOR_LEN; - return 0; - } -#endif /* CONFIG_PEERKEY */ - #ifdef CONFIG_IEEE80211W if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { @@ -908,14 +1030,14 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) if (*pos == WLAN_EID_RSN) { ie->rsn_ie = pos; ie->rsn_ie_len = pos[1] + 2; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { ie->mdie = pos; ie->mdie_len = pos[1] + 2; } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) { ie->ftie = pos; ie->ftie_len = pos[1] + 2; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { ret = wpa_parse_generic(pos, end, ie); if (ret < 0) @@ -938,3 +1060,36 @@ int wpa_auth_uses_mfp(struct wpa_state_machine *sm) { return sm ? sm->mgmt_frame_prot : 0; } + + +#ifdef CONFIG_OWE +u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, + u8 *pos, size_t max_len, + const u8 *req_ies, size_t req_ies_len) +{ + int res; + struct wpa_auth_config *conf; + + if (!sm) + return pos; + conf = &sm->wpa_auth->conf; + +#ifdef CONFIG_TESTING_OPTIONS + if (conf->own_ie_override_len) { + if (max_len < conf->own_ie_override_len) + return NULL; + wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing", + conf->own_ie_override, conf->own_ie_override_len); + os_memcpy(pos, conf->own_ie_override, + conf->own_ie_override_len); + return pos + conf->own_ie_override_len; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + res = wpa_write_rsn_ie(conf, pos, max_len, + sm->pmksa ? sm->pmksa->pmkid : NULL); + if (res < 0) + return pos; + return pos + res; +} +#endif /* CONFIG_OWE */ diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h index d2067ba3112c..73e433349049 100644 --- a/src/ap/wpa_auth_ie.h +++ b/src/ap/wpa_auth_ie.h @@ -19,26 +19,16 @@ struct wpa_eapol_ie_parse { size_t gtk_len; const u8 *mac_addr; size_t mac_addr_len; -#ifdef CONFIG_PEERKEY - const u8 *smk; - size_t smk_len; - const u8 *nonce; - size_t nonce_len; - const u8 *lifetime; - size_t lifetime_len; - const u8 *error; - size_t error_len; -#endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211W const u8 *igtk; size_t igtk_len; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP const u8 *mdie; size_t mdie_len; const u8 *ftie; size_t ftie_len; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_P2P const u8 *ip_addr_req; const u8 *ip_addr_alloc; diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index 95b40da0f6bb..5ec019971f37 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -1064,7 +1064,9 @@ int hostapd_init_wps(struct hostapd_data *hapd, if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) wps->auth_types |= WPS_AUTH_WPA2; - if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) { + if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | + WPA_CIPHER_CCMP_256 | + WPA_CIPHER_GCMP_256)) { wps->encr_types |= WPS_ENCR_AES; wps->encr_types_rsn |= WPS_ENCR_AES; } diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c index e0769c08e764..0b596bbcc631 100644 --- a/src/common/common_module_tests.c +++ b/src/common/common_module_tests.c @@ -53,12 +53,38 @@ static const struct ieee802_11_parse_test_data parse_tests[] = { 18, ParseOK, 9 }, { (u8 *) "\x8b\x00", 2, ParseOK, 1 }, { (u8 *) "\xdd\x04\x00\x90\x4c\x04", 6, ParseUnknown, 1 }, + { (u8 *) "\xed\x00", 2, ParseOK, 1 }, + { (u8 *) "\xef\x00", 2, ParseOK, 1 }, + { (u8 *) "\xef\x01\x11", 3, ParseOK, 1 }, + { (u8 *) "\xf0\x00", 2, ParseOK, 1 }, + { (u8 *) "\xf1\x00", 2, ParseOK, 1 }, + { (u8 *) "\xf1\x02\x11\x22", 4, ParseOK, 1 }, + { (u8 *) "\xf2\x00", 2, ParseOK, 1 }, + { (u8 *) "\xff\x00", 2, ParseUnknown, 1 }, + { (u8 *) "\xff\x01\x00", 3, ParseUnknown, 1 }, + { (u8 *) "\xff\x01\x01", 3, ParseOK, 1 }, + { (u8 *) "\xff\x02\x01\x00", 4, ParseOK, 1 }, + { (u8 *) "\xff\x01\x02", 3, ParseOK, 1 }, + { (u8 *) "\xff\x04\x02\x11\x22\x33", 6, ParseOK, 1 }, + { (u8 *) "\xff\x01\x04", 3, ParseOK, 1 }, + { (u8 *) "\xff\x01\x05", 3, ParseOK, 1 }, + { (u8 *) "\xff\x0d\x05\x11\x22\x33\x44\x55\x55\x11\x22\x33\x44\x55\x55", + 15, ParseOK, 1 }, + { (u8 *) "\xff\x01\x06", 3, ParseOK, 1 }, + { (u8 *) "\xff\x02\x06\x00", 4, ParseOK, 1 }, + { (u8 *) "\xff\x01\x07", 3, ParseOK, 1 }, + { (u8 *) "\xff\x09\x07\x11\x22\x33\x44\x55\x66\x77\x88", 11, + ParseOK, 1 }, + { (u8 *) "\xff\x01\x0c", 3, ParseOK, 1 }, + { (u8 *) "\xff\x02\x0c\x00", 4, ParseOK, 1 }, + { (u8 *) "\xff\x01\x0d", 3, ParseOK, 1 }, { NULL, 0, ParseOK, 0 } }; static int ieee802_11_parse_tests(void) { int i, ret = 0; + struct wpabuf *buf; wpa_printf(MSG_INFO, "ieee802_11_parse tests"); @@ -84,6 +110,35 @@ static int ieee802_11_parse_tests(void) ret = -1; } + buf = ieee802_11_vendor_ie_concat((const u8 *) "\xdd\x05\x11\x22\x33\x44\x01\xdd\x05\x11\x22\x33\x44\x02\x00\x01", + 16, 0x11223344); + do { + const u8 *pos; + + if (!buf) { + wpa_printf(MSG_ERROR, + "ieee802_11_vendor_ie_concat test 2 failed"); + ret = -1; + break; + } + + if (wpabuf_len(buf) != 2) { + wpa_printf(MSG_ERROR, + "ieee802_11_vendor_ie_concat test 3 failed"); + ret = -1; + break; + } + + pos = wpabuf_head(buf); + if (pos[0] != 0x01 || pos[1] != 0x02) { + wpa_printf(MSG_ERROR, + "ieee802_11_vendor_ie_concat test 3 failed"); + ret = -1; + break; + } + } while (0); + wpabuf_free(buf); + return ret; } diff --git a/src/common/ctrl_iface_common.c b/src/common/ctrl_iface_common.c index ebbe6ffdb385..e26407dab902 100644 --- a/src/common/ctrl_iface_common.c +++ b/src/common/ctrl_iface_common.c @@ -113,17 +113,53 @@ void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock, } +static int ctrl_set_events(struct wpa_ctrl_dst *dst, const char *input) +{ + const char *value; + int val; + + if (!input) + return 0; + + value = os_strchr(input, '='); + if (!value) + return -1; + value++; + val = atoi(value); + if (val < 0 || val > 1) + return -1; + + if (str_starts(input, "probe_rx_events=")) { + if (val) + dst->events |= WPA_EVENT_RX_PROBE_REQUEST; + else + dst->events &= ~WPA_EVENT_RX_PROBE_REQUEST; + } + + return 0; +} + + int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, - socklen_t fromlen) + socklen_t fromlen, const char *input) { struct wpa_ctrl_dst *dst; + /* Update event registration if already attached */ + dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) { + if (!sockaddr_compare(from, fromlen, + &dst->addr, dst->addrlen)) + return ctrl_set_events(dst, input); + } + + /* New attachment */ dst = os_zalloc(sizeof(*dst)); if (dst == NULL) return -1; os_memcpy(&dst->addr, from, fromlen); dst->addrlen = fromlen; dst->debug_level = MSG_INFO; + ctrl_set_events(dst, input); dl_list_add(ctrl_dst, &dst->list); sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor attached", from, fromlen); diff --git a/src/common/ctrl_iface_common.h b/src/common/ctrl_iface_common.h index 0b6e3e740291..85e258e938b6 100644 --- a/src/common/ctrl_iface_common.h +++ b/src/common/ctrl_iface_common.h @@ -11,6 +11,9 @@ #include "utils/list.h" +/* Events enable bits (wpa_ctrl_dst::events) */ +#define WPA_EVENT_RX_PROBE_REQUEST BIT(0) + /** * struct wpa_ctrl_dst - Data structure of control interface monitors * @@ -23,13 +26,14 @@ struct wpa_ctrl_dst { socklen_t addrlen; int debug_level; int errors; + u32 events; /* WPA_EVENT_* bitmap */ }; void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock, socklen_t socklen); int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, - socklen_t fromlen); + socklen_t fromlen, const char *input); int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, socklen_t fromlen); int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from, diff --git a/src/common/defs.h b/src/common/defs.h index 4f567945942e..c968cd6cb82a 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -1,6 +1,6 @@ /* * WPA Supplicant - Common definitions - * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -51,16 +51,28 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean; #define WPA_KEY_MGMT_OSEN BIT(15) #define WPA_KEY_MGMT_IEEE8021X_SUITE_B BIT(16) #define WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 BIT(17) +#define WPA_KEY_MGMT_FILS_SHA256 BIT(18) +#define WPA_KEY_MGMT_FILS_SHA384 BIT(19) +#define WPA_KEY_MGMT_FT_FILS_SHA256 BIT(20) +#define WPA_KEY_MGMT_FT_FILS_SHA384 BIT(21) +#define WPA_KEY_MGMT_OWE BIT(22) +#define WPA_KEY_MGMT_DPP BIT(23) +#define WPA_KEY_MGMT_FT_IEEE8021X_SHA384 BIT(24) static inline int wpa_key_mgmt_wpa_ieee8021x(int akm) { return !!(akm & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_FT_IEEE8021X | + WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | WPA_KEY_MGMT_CCKM | WPA_KEY_MGMT_OSEN | WPA_KEY_MGMT_IEEE8021X_SHA256 | WPA_KEY_MGMT_IEEE8021X_SUITE_B | - WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)); + WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 | + WPA_KEY_MGMT_FILS_SHA256 | + WPA_KEY_MGMT_FILS_SHA384 | + WPA_KEY_MGMT_FT_FILS_SHA256 | + WPA_KEY_MGMT_FT_FILS_SHA384)); } static inline int wpa_key_mgmt_wpa_psk(int akm) @@ -76,7 +88,15 @@ static inline int wpa_key_mgmt_ft(int akm) { return !!(akm & (WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_FT_IEEE8021X | - WPA_KEY_MGMT_FT_SAE)); + WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | + WPA_KEY_MGMT_FT_SAE | + WPA_KEY_MGMT_FT_FILS_SHA256 | + WPA_KEY_MGMT_FT_FILS_SHA384)); +} + +static inline int wpa_key_mgmt_ft_psk(int akm) +{ + return !!(akm & WPA_KEY_MGMT_FT_PSK); } static inline int wpa_key_mgmt_sae(int akm) @@ -85,17 +105,32 @@ static inline int wpa_key_mgmt_sae(int akm) WPA_KEY_MGMT_FT_SAE)); } +static inline int wpa_key_mgmt_fils(int akm) +{ + return !!(akm & (WPA_KEY_MGMT_FILS_SHA256 | + WPA_KEY_MGMT_FILS_SHA384 | + WPA_KEY_MGMT_FT_FILS_SHA256 | + WPA_KEY_MGMT_FT_FILS_SHA384)); +} + static inline int wpa_key_mgmt_sha256(int akm) { return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 | WPA_KEY_MGMT_IEEE8021X_SHA256 | + WPA_KEY_MGMT_SAE | + WPA_KEY_MGMT_FT_SAE | WPA_KEY_MGMT_OSEN | - WPA_KEY_MGMT_IEEE8021X_SUITE_B)); + WPA_KEY_MGMT_IEEE8021X_SUITE_B | + WPA_KEY_MGMT_FILS_SHA256 | + WPA_KEY_MGMT_FT_FILS_SHA256)); } static inline int wpa_key_mgmt_sha384(int akm) { - return !!(akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192); + return !!(akm & (WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 | + WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | + WPA_KEY_MGMT_FILS_SHA384 | + WPA_KEY_MGMT_FT_FILS_SHA384)); } static inline int wpa_key_mgmt_suite_b(int akm) @@ -108,7 +143,10 @@ static inline int wpa_key_mgmt_wpa(int akm) { return wpa_key_mgmt_wpa_ieee8021x(akm) || wpa_key_mgmt_wpa_psk(akm) || - wpa_key_mgmt_sae(akm); + wpa_key_mgmt_fils(akm) || + wpa_key_mgmt_sae(akm) || + akm == WPA_KEY_MGMT_OWE || + akm == WPA_KEY_MGMT_DPP; } static inline int wpa_key_mgmt_wpa_any(int akm) @@ -132,7 +170,13 @@ static inline int wpa_key_mgmt_cckm(int akm) #define WPA_AUTH_ALG_LEAP BIT(2) #define WPA_AUTH_ALG_FT BIT(3) #define WPA_AUTH_ALG_SAE BIT(4) +#define WPA_AUTH_ALG_FILS BIT(5) +#define WPA_AUTH_ALG_FILS_SK_PFS BIT(6) +static inline int wpa_auth_alg_fils(int alg) +{ + return !!(alg & (WPA_AUTH_ALG_FILS | WPA_AUTH_ALG_FILS_SK_PFS)); +} enum wpa_alg { WPA_ALG_NONE, @@ -341,4 +385,18 @@ enum wpa_radio_work_band { BAND_60_GHZ = BIT(2), }; +enum beacon_rate_type { + BEACON_RATE_LEGACY, + BEACON_RATE_HT, + BEACON_RATE_VHT +}; + +enum eap_proxy_sim_state { + SIM_STATE_ERROR, +}; + +#define OCE_STA BIT(0) +#define OCE_STA_CFON BIT(1) +#define OCE_AP BIT(2) + #endif /* DEFS_H */ diff --git a/src/common/dhcp.h b/src/common/dhcp.h new file mode 100644 index 000000000000..e38512c24d9f --- /dev/null +++ b/src/common/dhcp.h @@ -0,0 +1,263 @@ +/* + * DHCP definitions + * Copyright (c) 2014-2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DHCP_H +#define DHCP_H + +#include <netinet/ip.h> +#if __FAVOR_BSD +#include <netinet/udp.h> +#else +#define __FAVOR_BSD 1 +#include <netinet/udp.h> +#undef __FAVOR_BSD +#endif + +#define DHCP_SERVER_PORT 67 +#define DHCP_CLIENT_PORT 68 + +struct dhcp_data { + u8 op; + u8 htype; + u8 hlen; + u8 hops; + be32 xid; + be16 secs; + be16 flags; + be32 client_ip; + be32 your_ip; + be32 server_ip; + be32 relay_ip; + u8 hw_addr[16]; + u8 serv_name[64]; + u8 boot_file[128]; +} STRUCT_PACKED; + +struct bootp_pkt { + struct iphdr iph; + struct udphdr udph; + u8 op; + u8 htype; + u8 hlen; + u8 hops; + be32 xid; + be16 secs; + be16 flags; + be32 client_ip; + be32 your_ip; + be32 server_ip; + be32 relay_ip; + u8 hw_addr[16]; + u8 serv_name[64]; + u8 boot_file[128]; + u8 exten[312]; +} STRUCT_PACKED; + +#define DHCP_MAGIC 0x63825363 + +/* + * IANA DHCP/BOOTP registry + * http://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml +*/ +enum dhcp_options { + DHCP_OPT_PAD = 0, + DHCP_OPT_SUBNET_MASK = 1, + DHCP_OPT_TIME_OFFSET = 2, + DHCP_OPT_ROUTER = 3, + DHCP_OPT_TIME_SERVER = 4, + DHCP_OPT_NAME_SERVER = 5, + DHCP_OPT_DOMAIN_NAME_SERVER = 6, + DHCP_OPT_LOG_SERVER = 7, + DHCP_OPT_QUOTES_SERVER = 8, + DHCP_OPT_LPR_SERVER = 9, + DHCP_OPT_IMPRESS_SERVER = 10, + DHCP_OPT_RLP_SERVER = 11, + DHCP_OPT_HOSTNAME = 12, + DHCP_OPT_BOOT_FILE_SIZE = 13, + DHCP_OPT_MERIT_DUMP_FILE = 14, + DHCP_OPT_DOMAIN_NAME = 15, + DHCP_OPT_SWAP_SERVER = 16, + DHCP_OPT_ROOT_PATH = 17, + DHCP_OPT_EXTENSION_PATH = 18, + DHCP_OPT_FORWARD = 19, + DHCP_OPT_SRC_RTE = 20, + DHCP_OPT_POLICY_FILTER = 21, + DHCP_OPT_MAX_DG_ASSEMBLY = 22, + DHCP_OPT_DEFAULT_IP_TTL = 23, + DHCP_OPT_MTU_TIMEOUT = 24, + DHCP_OPT_MTU_PLATEAU = 25, + DHCP_OPT_MTU_INTERFACE = 26, + DHCP_OPT_ALL_SUBNETS_LOCAL = 27, + DHCP_OPT_BROADCAST_ADDRESS = 28, + DHCP_OPT_MASK_DISCOVERY = 29, + DHCP_OPT_MASK_SUPPLIER = 30, + DHCP_OPT_ROUTER_DISCOVERY = 31, + DHCP_OPT_ROUTER_SOLICITATION_ADDRESS = 32, + DHCP_OPT_STATIC_ROUTE = 33, + DHCP_OPT_TRAILERS = 34, + DHCP_OPT_ARP_TIMEOUT = 35, + DHCP_OPT_ETHERNET = 36, + DHCP_OPT_TCP_DEFAULT_TTL = 37, + DHCP_OPT_TCP_KEEPALIVE_INTERVAL = 38, + DHCP_OPT_TCP_KEEPALIVE_GARBAGE = 39, + DHCP_OPT_NIS_DOMAIN = 40, + DHCP_OPT_NIS_SERVERS = 41, + DHCP_OPT_NTP_SERVERS = 42, + DHCP_OPT_VENDOR_SPECIFIC = 43, + DHCP_OPT_NETBIOS_NAME_SERVER = 44, + DHCP_OPT_NETBIOS_DISTRIBUTION_SERVER = 45, + DHCP_OPT_NETBIOS_NODE_TYPE = 46, + DHCP_OPT_NETBIOS_SCOPE = 47, + DHCP_OPT_FONT_SERVER = 48, + DHCP_OPT_DISPLAY_MANAGER = 49, + DHCP_OPT_REQUESTED_IP_ADDRESS = 50, + DHCP_OPT_IP_ADDRESS_LEASE_TIME = 51, + DHCP_OPT_OVERLOAD = 52, + DHCP_OPT_MSG_TYPE = 53, + DHCP_OPT_SERVER_ID = 54, + DHCP_OPT_PARAMETER_REQ_LIST = 55, + DHCP_OPT_MESSAGE = 56, + DHCP_OPT_MAX_MESSAGE_SIZE = 57, + DHCP_OPT_RENEWAL_TIME = 58, + DHCP_OPT_REBINDING_TIME = 59, + DHCP_OPT_VENDOR_CLASS_ID = 60, + DHCP_OPT_CLIENT_ID = 61, + DHCP_OPT_NETWARE_IP_DOMAIN = 62, + DHCP_OPT_NETWARE_IP_OPTION = 63, + DHCP_OPT_NIS_V3_DOMAIN = 64, + DHCP_OPT_NIS_V3_SERVERS = 65, + DHCP_OPT_TFTP_SERVER_NAME = 66, + DHCP_OPT_BOOT_FILE_NAME = 67, + DHCP_OPT_HOME_AGENT_ADDRESSES = 68, + DHCP_OPT_SMTP_SERVER = 69, + DHCP_OPT_POP3_SERVER = 70, + DHCP_OPT_NNTP_SERVER = 71, + DHCP_OPT_WWW_SERVER = 72, + DHCP_OPT_FINGER_SERVER = 73, + DHCP_OPT_IRC_SERVER = 74, + DHCP_OPT_STREETTALK_SERVER = 75, + DHCP_OPT_STDA_SERVER = 76, + DHCP_OPT_USER_CLASS = 77, + DHCP_OPT_DIRECTORY_AGENT = 78, + DHCP_OPT_SERVICE_SCOPE = 79, + DHCP_OPT_RAPID_COMMIT = 80, + DHCP_OPT_CLIENT_FQDN = 81, + DHCP_OPT_RELAY_AGENT_INFO = 82, + DHCP_OPT_ISNS = 83, + DHCP_OPT_NDS_SERVERS = 85, + DHCP_OPT_NDS_TREE_NAME = 86, + DHCP_OPT_NDS_CONTEXT = 87, + DHCP_OPT_BCMCS_CONTROLLER_DOMAIN_NAME_LIST = 88, + DHCP_OPT_BCMCS_CONTROLLER_IPV4_ADDRESS = 89, + DHCP_OPT_AUTHENTICATION = 90, + DHCP_OPT_CLIENT_LAST_TRANSACTION_TIME = 91, + DHCP_OPT_ASSOCIATED_IP = 92, + DHCP_OPT_CLIENT_SYSYEM = 93, + DHCP_OPT_CLIENT_NDI = 94, + DHCP_OPT_LDAP = 95, + DHCP_OPT_UUID_GUID = 97, + DHCP_OPT_USER_AUTH = 98, + DHCP_OPT_GEOCONF_CIVIC = 99, + DHCP_OPT_PCODE = 100, + DHCP_OPT_TCODE = 101, + DHCP_OPT_NETINFO_ADDRESS = 112, + DHCP_OPT_NETINFO_TAG = 113, + DHCP_OPT_URL = 114, + DHCP_OPT_AUTO_CONFIG = 116, + DHCP_OPT_NAME_SERVICE_SEARCH = 117, + DHCP_OPT_SUBNET_SELECTION = 118, + DHCP_OPT_DOMAIN_SEARCH = 119, + DHCP_OPT_SIP_SERVERS_DCP = 120, + DHCP_OPT_CLASSLESS_STATIC_ROUTE = 121, + DHCP_OPT_CCC = 122, + DHCP_OPT_GEOCONF = 123, + DHCP_OPT_V_I_VENDOR_CLASS = 124, + DHCP_OPT_V_I_VENDOR_SPECIFIC_INFO = 125, + DHCP_OPT_PANA_AGENT = 136, + DHCP_OPT_V4_LOST = 137, + DHCP_OPT_CAPWAP_AC_V4 = 138, + DHCP_OPT_IPV4_ADDRESS_MOS = 139, + DHCP_OPT_IPV4_FQDN_MOS = 140, + DHCP_OPT_SIP_UA_CONF = 141, + DHCP_OPT_IPV4_ADDRESS_ANDSF = 142, + DHCP_OPT_GEOLOC = 144, + DHCP_OPT_FORCERENEW_NONCE_CAPABLE = 145, + DHCP_OPT_RDNSS_SELECTION = 146, + DHCP_OPT_TFTP_SERVER_ADDRESS = 150, + DHCP_OPT_STATUS_CODE = 151, + DHCP_OPT_BASE_TIME = 152, + DHCP_OPT_START_TIME_OF_STATE = 153, + DHCP_OPT_QUERY_START_TIME = 154, + DHCP_OPT_QUERY_END_TIME = 155, + DHCP_OPT_STATE = 156, + DHCP_OPT_DATA_SOURCE = 157, + DHCP_OPT_V4_PCP_SERVER = 158, + DHCP_OPT_V4_PORTPARAMS = 159, + DHCP_OPT_CAPTIVE_PORTAL = 160, + DHCP_OPT_CONF_FILE = 209, + DHCP_OPT_PATH_PREFIX = 210, + DHCP_OPT_REBOOT_TIME = 211, + DHCP_OPT_6RD = 212, + DHCP_OPT_V4_ACCESS_DOMAIN = 213, + DHCP_OPT_SUBNET_ALLOCATION = 220, + DHCP_OPT_VSS = 221, + DHCP_OPT_END = 255 +}; + +enum dhcp_message_types { + DHCPDISCOVER = 1, + DHCPOFFER = 2, + DHCPREQUEST = 3, + DHCPDECLINE = 4, + DHCPACK = 5, + DHCPNAK = 6, + DHCPRELEASE = 7, + DHCPINFORM = 8, + DHCPFORCERENEW = 9, + DHCPLEASEQUERY = 10, + DHCPLEASEUNASSIGNED = 11, + DHCPLEASEUNKNOWN = 12, + DHCPLEASEACTIVE = 13, + DHCPBULKLEASEQUERY = 14, + DHCPLEASEQUERYDONE = 15, + DHCPACTIVELEASEQUERY = 16, + DHCPLEASEQUERYSTATUS = 17, + DHCPTLS = 18, +}; + +enum dhcp_relay_agent_suboptions { + DHCP_RELAY_OPT_AGENT_CIRCUIT_ID = 1, + DHCP_RELAY_OPT_AGENT_REMOTE_ID = 2, + DHCP_RELAY_OPT_DOCSIS_DEVICE_CLASS = 4, + DHCP_RELAY_OPT_LINK_SELECTION = 5, + DHCP_RELAY_OPT_SUBSCRIBE_ID = 6, + DHCP_RELAY_OPT_RADIUS_ATTRIBUTES = 7, + DHCP_RELAY_OPT_AUTHENTICATION = 8, + DHCP_RELAY_OPT_VEDOR_SPECIFIC = 9, + DHCP_RELAY_OPT_RELAY_AGENT_FLAGS = 10, + DHCP_RELAY_OPT_SERVER_ID_OVERRIDE = 11, + DHCP_RELAY_OPT_RELAY_AGENT_ID = 12, + DHCP_RELAY_OPT_ACCESS_TECHNOLOGY_TYPE = 13, + DHCP_RELAY_OPT_ACCESS_NETWORK_NAME = 14, + DHCP_RELAY_OPT_ACCESS_POINT_NAME = 15, + DHCP_RELAY_OPT_ACCESS_POINT_BSSID = 16, + DHCP_RELAY_OPT_OPERATOR_ID = 17, + DHCP_RELAY_OPT_OPERATOR_REALM = 18, + DHCP_RELAY_OPT_DHCPV4_VIRTUAL_SUBNET_SELECTION = 151, + DHCP_RELAY_OPT_DHCPV4_VIRTUAL_SUBNET_SELECTION_CONTROL = 152, +}; + +enum access_technology_types { + ACCESS_TECHNOLOGY_VIRTUAL = 1, + ACCESS_TECHNOLOGY_PPP = 2, + ACCESS_TECHNOLOGY_ETHERNET = 3, + ACCESS_TECHNOLOGY_WLAN = 4, + ACCESS_TECHNOLOGY_WIMAX = 5, +}; + +#endif /* DHCP_H */ diff --git a/src/common/dpp.c b/src/common/dpp.c new file mode 100644 index 000000000000..e715e0454d72 --- /dev/null +++ b/src/common/dpp.c @@ -0,0 +1,7691 @@ +/* + * DPP functionality shared between hostapd and wpa_supplicant + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include <openssl/opensslv.h> +#include <openssl/err.h> +#include <openssl/asn1.h> +#include <openssl/asn1t.h> + +#include "utils/common.h" +#include "utils/base64.h" +#include "utils/json.h" +#include "common/ieee802_11_common.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" +#include "crypto/crypto.h" +#include "crypto/random.h" +#include "crypto/aes.h" +#include "crypto/aes_siv.h" +#include "crypto/sha384.h" +#include "crypto/sha512.h" +#include "drivers/driver.h" +#include "dpp.h" + + +#ifdef CONFIG_TESTING_OPTIONS +enum dpp_test_behavior dpp_test = DPP_TEST_DISABLED; +u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; +u8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; +u8 dpp_pkex_ephemeral_key_override[600]; +size_t dpp_pkex_ephemeral_key_override_len = 0; +u8 dpp_protocol_key_override[600]; +size_t dpp_protocol_key_override_len = 0; +u8 dpp_nonce_override[DPP_MAX_NONCE_LEN]; +size_t dpp_nonce_override_len = 0; + +static int dpp_test_gen_invalid_key(struct wpabuf *msg, + const struct dpp_curve_params *curve); +#endif /* CONFIG_TESTING_OPTIONS */ + +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) +/* Compatibility wrappers for older versions. */ + +static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) +{ + sig->r = r; + sig->s = s; + return 1; +} + + +static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, + const BIGNUM **ps) +{ + if (pr) + *pr = sig->r; + if (ps) + *ps = sig->s; +} + +#endif + + +static const struct dpp_curve_params dpp_curves[] = { + /* The mandatory to support and the default NIST P-256 curve needs to + * be the first entry on this list. */ + { "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" }, + { "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" }, + { "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" }, + { "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" }, + { "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" }, + { "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" }, + { NULL, 0, 0, 0, 0, NULL, 0, NULL } +}; + + +/* Role-specific elements for PKEX */ + +/* NIST P-256 */ +static const u8 pkex_init_x_p256[32] = { + 0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b, + 0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54, + 0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07, + 0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25 + }; +static const u8 pkex_init_y_p256[32] = { + 0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b, + 0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc, + 0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45, + 0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4 + }; +static const u8 pkex_resp_x_p256[32] = { + 0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39, + 0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f, + 0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f, + 0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76 +}; +static const u8 pkex_resp_y_p256[32] = { + 0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19, + 0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1, + 0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a, + 0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67 +}; + +/* NIST P-384 */ +static const u8 pkex_init_x_p384[48] = { + 0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa, + 0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68, + 0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53, + 0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac, + 0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12, + 0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3 +}; +static const u8 pkex_init_y_p384[48] = { + 0x76, 0x2f, 0x68, 0x84, 0xa6, 0xb0, 0x59, 0x29, + 0x83, 0xa2, 0x6c, 0xa4, 0x6c, 0x3b, 0xf8, 0x56, + 0x76, 0x11, 0x2a, 0x32, 0x90, 0xbd, 0x07, 0xc7, + 0x37, 0x39, 0x9d, 0xdb, 0x96, 0xf3, 0x2b, 0xb6, + 0x27, 0xbb, 0x29, 0x3c, 0x17, 0x33, 0x9d, 0x94, + 0xc3, 0xda, 0xac, 0x46, 0xb0, 0x8e, 0x07, 0x18 +}; +static const u8 pkex_resp_x_p384[48] = { + 0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98, + 0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97, + 0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92, + 0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44, + 0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf, + 0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf +}; +static const u8 pkex_resp_y_p384[48] = { + 0xab, 0xa7, 0xdf, 0x52, 0xaa, 0xe2, 0x35, 0x0c, + 0xe3, 0x75, 0x32, 0xe6, 0xbf, 0x06, 0xc8, 0x7c, + 0x38, 0x29, 0x4c, 0xec, 0x82, 0xac, 0xd7, 0xa3, + 0x09, 0xd2, 0x0e, 0x22, 0x5a, 0x74, 0x52, 0xa1, + 0x7e, 0x54, 0x4e, 0xfe, 0xc6, 0x29, 0x33, 0x63, + 0x15, 0xe1, 0x7b, 0xe3, 0x40, 0x1c, 0xca, 0x06 +}; + +/* NIST P-521 */ +static const u8 pkex_init_x_p521[66] = { + 0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23, + 0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0, + 0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76, + 0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5, + 0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38, + 0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01, + 0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e, + 0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d, + 0x97, 0x76 +}; +static const u8 pkex_init_y_p521[66] = { + 0x00, 0xb3, 0x8e, 0x02, 0xe4, 0x2a, 0x63, 0x59, + 0x12, 0xc6, 0x10, 0xba, 0x3a, 0xf9, 0x02, 0x99, + 0x3f, 0x14, 0xf0, 0x40, 0xde, 0x5c, 0xc9, 0x8b, + 0x02, 0x55, 0xfa, 0x91, 0xb1, 0xcc, 0x6a, 0xbd, + 0xe5, 0x62, 0xc0, 0xc5, 0xe3, 0xa1, 0x57, 0x9f, + 0x08, 0x1a, 0xa6, 0xe2, 0xf8, 0x55, 0x90, 0xbf, + 0xf5, 0xa6, 0xc3, 0xd8, 0x52, 0x1f, 0xb7, 0x02, + 0x2e, 0x7c, 0xc8, 0xb3, 0x20, 0x1e, 0x79, 0x8d, + 0x03, 0xa8 +}; +static const u8 pkex_resp_x_p521[66] = { + 0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a, + 0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44, + 0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f, + 0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb, + 0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48, + 0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e, + 0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a, + 0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97, + 0x84, 0xb4 +}; +static const u8 pkex_resp_y_p521[66] = { + 0x00, 0x46, 0x63, 0x39, 0xbe, 0xcd, 0xa4, 0x2d, + 0xca, 0x27, 0x74, 0xd4, 0x1b, 0x91, 0x33, 0x20, + 0x83, 0xc7, 0x3b, 0xa4, 0x09, 0x8b, 0x8e, 0xa3, + 0x88, 0xe9, 0x75, 0x7f, 0x56, 0x7b, 0x38, 0x84, + 0x62, 0x02, 0x7c, 0x90, 0x51, 0x07, 0xdb, 0xe9, + 0xd0, 0xde, 0xda, 0x9a, 0x5d, 0xe5, 0x94, 0xd2, + 0xcf, 0x9d, 0x4c, 0x33, 0x91, 0xa6, 0xc3, 0x80, + 0xa7, 0x6e, 0x7e, 0x8d, 0xf8, 0x73, 0x6e, 0x53, + 0xce, 0xe1 +}; + +/* Brainpool P-256r1 */ +static const u8 pkex_init_x_bp_p256r1[32] = { + 0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10, + 0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca, + 0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75, + 0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8 +}; +static const u8 pkex_init_y_bp_p256r1[32] = { + 0x93, 0xca, 0xef, 0xa9, 0x66, 0x3e, 0x87, 0xcd, + 0x52, 0x6e, 0x54, 0x13, 0xef, 0x31, 0x67, 0x30, + 0x15, 0x13, 0x9d, 0x6d, 0xc0, 0x95, 0x32, 0xbe, + 0x4f, 0xab, 0x5d, 0xf7, 0xbf, 0x5e, 0xaa, 0x0b +}; +static const u8 pkex_resp_x_bp_p256r1[32] = { + 0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f, + 0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a, + 0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a, + 0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3 +}; +static const u8 pkex_resp_y_bp_p256r1[32] = { + 0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd, + 0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2, + 0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e, + 0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64 +}; + +/* Brainpool P-384r1 */ +static const u8 pkex_init_x_bp_p384r1[48] = { + 0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd, + 0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19, + 0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06, + 0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62, + 0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30, + 0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe +}; +static const u8 pkex_init_y_bp_p384r1[48] = { + 0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99, + 0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86, + 0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32, + 0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9, + 0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e, + 0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52 +}; +static const u8 pkex_resp_x_bp_p384r1[48] = { + 0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0, + 0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25, + 0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b, + 0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71, + 0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce, + 0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c +}; +static const u8 pkex_resp_y_bp_p384r1[48] = { + 0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65, + 0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04, + 0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70, + 0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c, + 0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb, + 0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1 +}; + +/* Brainpool P-512r1 */ +static const u8 pkex_init_x_bp_p512r1[64] = { + 0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c, + 0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51, + 0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc, + 0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95, + 0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d, + 0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff, + 0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc, + 0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f +}; +static const u8 pkex_init_y_bp_p512r1[64] = { + 0x50, 0xb5, 0x9b, 0xfa, 0x45, 0x67, 0x75, 0x94, + 0x44, 0xe7, 0x68, 0xb0, 0xeb, 0x3e, 0xb3, 0xb8, + 0xf9, 0x99, 0x05, 0xef, 0xae, 0x6c, 0xbc, 0xe3, + 0xe1, 0xd2, 0x51, 0x54, 0xdf, 0x59, 0xd4, 0x45, + 0x41, 0x3a, 0xa8, 0x0b, 0x76, 0x32, 0x44, 0x0e, + 0x07, 0x60, 0x3a, 0x6e, 0xbe, 0xfe, 0xe0, 0x58, + 0x52, 0xa0, 0xaa, 0x8b, 0xd8, 0x5b, 0xf2, 0x71, + 0x11, 0x9a, 0x9e, 0x8f, 0x1a, 0xd1, 0xc9, 0x99 +}; +static const u8 pkex_resp_x_bp_p512r1[64] = { + 0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72, + 0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76, + 0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19, + 0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e, + 0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9, + 0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88, + 0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29, + 0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e +}; +static const u8 pkex_resp_y_bp_p512r1[64] = { + 0x80, 0x1f, 0x43, 0xd2, 0x17, 0x35, 0xec, 0x81, + 0xd9, 0x4b, 0xdc, 0x81, 0x19, 0xd9, 0x5f, 0x68, + 0x16, 0x84, 0xfe, 0x63, 0x4b, 0x8d, 0x5d, 0xaa, + 0x88, 0x4a, 0x47, 0x48, 0xd4, 0xea, 0xab, 0x7d, + 0x6a, 0xbf, 0xe1, 0x28, 0x99, 0x6a, 0x87, 0x1c, + 0x30, 0xb4, 0x44, 0x2d, 0x75, 0xac, 0x35, 0x09, + 0x73, 0x24, 0x3d, 0xb4, 0x43, 0xb1, 0xc1, 0x56, + 0x56, 0xad, 0x30, 0x87, 0xf4, 0xc3, 0x00, 0xc7 +}; + + +static void dpp_debug_print_point(const char *title, const EC_GROUP *group, + const EC_POINT *point) +{ + BIGNUM *x, *y; + BN_CTX *ctx; + char *x_str = NULL, *y_str = NULL; + + if (!wpa_debug_show_keys) + return; + + ctx = BN_CTX_new(); + x = BN_new(); + y = BN_new(); + if (!ctx || !x || !y || + EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1) + goto fail; + + x_str = BN_bn2hex(x); + y_str = BN_bn2hex(y); + if (!x_str || !y_str) + goto fail; + + wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str); + +fail: + OPENSSL_free(x_str); + OPENSSL_free(y_str); + BN_free(x); + BN_free(y); + BN_CTX_free(ctx); +} + + +static int dpp_hash_vector(const struct dpp_curve_params *curve, + size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + if (curve->hash_len == 32) + return sha256_vector(num_elem, addr, len, mac); + if (curve->hash_len == 48) + return sha384_vector(num_elem, addr, len, mac); + if (curve->hash_len == 64) + return sha512_vector(num_elem, addr, len, mac); + return -1; +} + + +static int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len, + const char *label, u8 *out, size_t outlen) +{ + if (hash_len == 32) + return hmac_sha256_kdf(secret, secret_len, NULL, + (const u8 *) label, os_strlen(label), + out, outlen); + if (hash_len == 48) + return hmac_sha384_kdf(secret, secret_len, NULL, + (const u8 *) label, os_strlen(label), + out, outlen); + if (hash_len == 64) + return hmac_sha512_kdf(secret, secret_len, NULL, + (const u8 *) label, os_strlen(label), + out, outlen); + return -1; +} + + +static int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len, + size_t num_elem, const u8 *addr[], + const size_t *len, u8 *mac) +{ + if (hash_len == 32) + return hmac_sha256_vector(key, key_len, num_elem, addr, len, + mac); + if (hash_len == 48) + return hmac_sha384_vector(key, key_len, num_elem, addr, len, + mac); + if (hash_len == 64) + return hmac_sha512_vector(key, key_len, num_elem, addr, len, + mac); + return -1; +} + + +static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len, + const u8 *data, size_t data_len, u8 *mac) +{ + if (hash_len == 32) + return hmac_sha256(key, key_len, data, data_len, mac); + if (hash_len == 48) + return hmac_sha384(key, key_len, data, data_len, mac); + if (hash_len == 64) + return hmac_sha512(key, key_len, data, data_len, mac); + return -1; +} + + +static int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len) +{ + int num_bytes, offset; + + num_bytes = BN_num_bytes(bn); + if ((size_t) num_bytes > len) + return -1; + offset = len - num_bytes; + os_memset(pos, 0, offset); + BN_bn2bin(bn, pos + offset); + return 0; +} + + +static struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix) +{ + int len, res; + EC_KEY *eckey; + struct wpabuf *buf; + unsigned char *pos; + + eckey = EVP_PKEY_get1_EC_KEY(pkey); + if (!eckey) + return NULL; + EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED); + len = i2o_ECPublicKey(eckey, NULL); + if (len <= 0) { + wpa_printf(MSG_ERROR, + "DDP: Failed to determine public key encoding length"); + EC_KEY_free(eckey); + return NULL; + } + + buf = wpabuf_alloc(len); + if (!buf) { + EC_KEY_free(eckey); + return NULL; + } + + pos = wpabuf_put(buf, len); + res = i2o_ECPublicKey(eckey, &pos); + EC_KEY_free(eckey); + if (res != len) { + wpa_printf(MSG_ERROR, + "DDP: Failed to encode public key (res=%d/%d)", + res, len); + wpabuf_free(buf); + return NULL; + } + + if (!prefix) { + /* Remove 0x04 prefix to match DPP definition */ + pos = wpabuf_mhead(buf); + os_memmove(pos, pos + 1, len - 1); + buf->used--; + } + + return buf; +} + + +static EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group, + const u8 *buf_x, const u8 *buf_y, + size_t len) +{ + EC_KEY *eckey = NULL; + BN_CTX *ctx; + EC_POINT *point = NULL; + BIGNUM *x = NULL, *y = NULL; + EVP_PKEY *pkey = NULL; + + ctx = BN_CTX_new(); + if (!ctx) { + wpa_printf(MSG_ERROR, "DPP: Out of memory"); + return NULL; + } + + point = EC_POINT_new(group); + x = BN_bin2bn(buf_x, len, NULL); + y = BN_bin2bn(buf_y, len, NULL); + if (!point || !x || !y) { + wpa_printf(MSG_ERROR, "DPP: Out of memory"); + goto fail; + } + + if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) { + wpa_printf(MSG_ERROR, + "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (!EC_POINT_is_on_curve(group, point, ctx) || + EC_POINT_is_at_infinity(group, point)) { + wpa_printf(MSG_ERROR, "DPP: Invalid point"); + goto fail; + } + dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point); + + eckey = EC_KEY_new(); + if (!eckey || + EC_KEY_set_group(eckey, group) != 1 || + EC_KEY_set_public_key(eckey, point) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to set EC_KEY: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); + + pkey = EVP_PKEY_new(); + if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) { + wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY"); + goto fail; + } + +out: + BN_free(x); + BN_free(y); + EC_KEY_free(eckey); + EC_POINT_free(point); + BN_CTX_free(ctx); + return pkey; +fail: + EVP_PKEY_free(pkey); + pkey = NULL; + goto out; +} + + +static EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, + const u8 *buf, size_t len) +{ + EC_KEY *eckey; + const EC_GROUP *group; + EVP_PKEY *pkey = NULL; + + if (len & 1) + return NULL; + + eckey = EVP_PKEY_get1_EC_KEY(group_key); + if (!eckey) { + wpa_printf(MSG_ERROR, + "DPP: Could not get EC_KEY from group_key"); + return NULL; + } + + group = EC_KEY_get0_group(eckey); + if (group) + pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2, + len / 2); + else + wpa_printf(MSG_ERROR, "DPP: Could not get EC group"); + + EC_KEY_free(eckey); + return pkey; +} + + +static void dpp_auth_fail(struct dpp_authentication *auth, const char *txt) +{ + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt); +} + + +struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type, + size_t len) +{ + struct wpabuf *msg; + + msg = wpabuf_alloc(8 + len); + if (!msg) + return NULL; + wpabuf_put_u8(msg, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC); + wpabuf_put_be24(msg, OUI_WFA); + wpabuf_put_u8(msg, DPP_OUI_TYPE); + wpabuf_put_u8(msg, 1); /* Crypto Suite */ + wpabuf_put_u8(msg, type); + return msg; +} + + +const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len) +{ + u16 id, alen; + const u8 *pos = buf, *end = buf + len; + + while (end - pos >= 4) { + id = WPA_GET_LE16(pos); + pos += 2; + alen = WPA_GET_LE16(pos); + pos += 2; + if (alen > end - pos) + return NULL; + if (id == req_id) { + *ret_len = alen; + return pos; + } + pos += alen; + } + + return NULL; +} + + +int dpp_check_attrs(const u8 *buf, size_t len) +{ + const u8 *pos, *end; + int wrapped_data = 0; + + pos = buf; + end = buf + len; + while (end - pos >= 4) { + u16 id, alen; + + id = WPA_GET_LE16(pos); + pos += 2; + alen = WPA_GET_LE16(pos); + pos += 2; + wpa_printf(MSG_MSGDUMP, "DPP: Attribute ID %04x len %u", + id, alen); + if (alen > end - pos) { + wpa_printf(MSG_DEBUG, + "DPP: Truncated message - not enough room for the attribute - dropped"); + return -1; + } + if (wrapped_data) { + wpa_printf(MSG_DEBUG, + "DPP: An unexpected attribute included after the Wrapped Data attribute"); + return -1; + } + if (id == DPP_ATTR_WRAPPED_DATA) + wrapped_data = 1; + pos += alen; + } + + if (end != pos) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected octets (%d) after the last attribute", + (int) (end - pos)); + return -1; + } + + return 0; +} + + +void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info) +{ + if (!info) + return; + os_free(info->uri); + os_free(info->info); + EVP_PKEY_free(info->pubkey); + os_free(info); +} + + +const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type) +{ + switch (type) { + case DPP_BOOTSTRAP_QR_CODE: + return "QRCODE"; + case DPP_BOOTSTRAP_PKEX: + return "PKEX"; + } + return "??"; +} + + +static int dpp_uri_valid_info(const char *info) +{ + while (*info) { + unsigned char val = *info++; + + if (val < 0x20 || val > 0x7e || val == 0x3b) + return 0; + } + + return 1; +} + + +static int dpp_clone_uri(struct dpp_bootstrap_info *bi, const char *uri) +{ + bi->uri = os_strdup(uri); + return bi->uri ? 0 : -1; +} + + +int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi, + const char *chan_list) +{ + const char *pos = chan_list; + int opclass, channel, freq; + + while (pos && *pos && *pos != ';') { + opclass = atoi(pos); + if (opclass <= 0) + goto fail; + pos = os_strchr(pos, '/'); + if (!pos) + goto fail; + pos++; + channel = atoi(pos); + if (channel <= 0) + goto fail; + while (*pos >= '0' && *pos <= '9') + pos++; + freq = ieee80211_chan_to_freq(NULL, opclass, channel); + wpa_printf(MSG_DEBUG, + "DPP: URI channel-list: opclass=%d channel=%d ==> freq=%d", + opclass, channel, freq); + if (freq < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Ignore unknown URI channel-list channel (opclass=%d channel=%d)", + opclass, channel); + } else if (bi->num_freq == DPP_BOOTSTRAP_MAX_FREQ) { + wpa_printf(MSG_DEBUG, + "DPP: Too many channels in URI channel-list - ignore list"); + bi->num_freq = 0; + break; + } else { + bi->freq[bi->num_freq++] = freq; + } + + if (*pos == ';' || *pos == '\0') + break; + if (*pos != ',') + goto fail; + pos++; + } + + return 0; +fail: + wpa_printf(MSG_DEBUG, "DPP: Invalid URI channel-list"); + return -1; +} + + +int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac) +{ + if (!mac) + return 0; + + if (hwaddr_aton2(mac, bi->mac_addr) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Invalid URI mac"); + return -1; + } + + wpa_printf(MSG_DEBUG, "DPP: URI mac: " MACSTR, MAC2STR(bi->mac_addr)); + + return 0; +} + + +int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info) +{ + const char *end; + + if (!info) + return 0; + + end = os_strchr(info, ';'); + if (!end) + end = info + os_strlen(info); + bi->info = os_malloc(end - info + 1); + if (!bi->info) + return -1; + os_memcpy(bi->info, info, end - info); + bi->info[end - info] = '\0'; + wpa_printf(MSG_DEBUG, "DPP: URI(information): %s", bi->info); + if (!dpp_uri_valid_info(bi->info)) { + wpa_printf(MSG_DEBUG, "DPP: Invalid URI information payload"); + return -1; + } + + return 0; +} + + +static const struct dpp_curve_params * +dpp_get_curve_oid(const ASN1_OBJECT *poid) +{ + ASN1_OBJECT *oid; + int i; + + for (i = 0; dpp_curves[i].name; i++) { + oid = OBJ_txt2obj(dpp_curves[i].name, 0); + if (oid && OBJ_cmp(poid, oid) == 0) + return &dpp_curves[i]; + } + return NULL; +} + + +static const struct dpp_curve_params * dpp_get_curve_nid(int nid) +{ + int i, tmp; + + if (!nid) + return NULL; + for (i = 0; dpp_curves[i].name; i++) { + tmp = OBJ_txt2nid(dpp_curves[i].name); + if (tmp == nid) + return &dpp_curves[i]; + } + return NULL; +} + + +static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info) +{ + const char *end; + u8 *data; + size_t data_len; + EVP_PKEY *pkey; + const unsigned char *p; + int res; + X509_PUBKEY *pub = NULL; + ASN1_OBJECT *ppkalg; + const unsigned char *pk; + int ppklen; + X509_ALGOR *pa; +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + ASN1_OBJECT *pa_oid; +#else + const ASN1_OBJECT *pa_oid; +#endif + const void *pval; + int ptype; + const ASN1_OBJECT *poid; + char buf[100]; + + end = os_strchr(info, ';'); + if (!end) + return -1; + + data = base64_decode((const unsigned char *) info, end - info, + &data_len); + if (!data) { + wpa_printf(MSG_DEBUG, + "DPP: Invalid base64 encoding on URI public-key"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "DPP: Base64 decoded URI public-key", + data, data_len); + + if (sha256_vector(1, (const u8 **) &data, &data_len, + bi->pubkey_hash) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key"); + os_free(data); + return -1; + } + wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", + bi->pubkey_hash, SHA256_MAC_LEN); + + /* DER encoded ASN.1 SubjectPublicKeyInfo + * + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + * + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL } + * + * subjectPublicKey = compressed format public key per ANSI X9.63 + * algorithm = ecPublicKey (1.2.840.10045.2.1) + * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g., + * prime256v1 (1.2.840.10045.3.1.7) + */ + + p = data; + pkey = d2i_PUBKEY(NULL, &p, data_len); + os_free(data); + + if (!pkey) { + wpa_printf(MSG_DEBUG, + "DPP: Could not parse URI public-key SubjectPublicKeyInfo"); + return -1; + } + + if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) { + wpa_printf(MSG_DEBUG, + "DPP: SubjectPublicKeyInfo does not describe an EC key"); + EVP_PKEY_free(pkey); + return -1; + } + + res = X509_PUBKEY_set(&pub, pkey); + if (res != 1) { + wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey"); + goto fail; + } + + res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub); + if (res != 1) { + wpa_printf(MSG_DEBUG, + "DPP: Could not extract SubjectPublicKeyInfo parameters"); + goto fail; + } + res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0); + if (res < 0 || (size_t) res >= sizeof(buf)) { + wpa_printf(MSG_DEBUG, + "DPP: Could not extract SubjectPublicKeyInfo algorithm"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf); + if (os_strcmp(buf, "id-ecPublicKey") != 0) { + wpa_printf(MSG_DEBUG, + "DPP: Unsupported SubjectPublicKeyInfo algorithm"); + goto fail; + } + + X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa); + if (ptype != V_ASN1_OBJECT) { + wpa_printf(MSG_DEBUG, + "DPP: SubjectPublicKeyInfo parameters did not contain an OID"); + goto fail; + } + poid = pval; + res = OBJ_obj2txt(buf, sizeof(buf), poid, 0); + if (res < 0 || (size_t) res >= sizeof(buf)) { + wpa_printf(MSG_DEBUG, + "DPP: Could not extract SubjectPublicKeyInfo parameters OID"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf); + bi->curve = dpp_get_curve_oid(poid); + if (!bi->curve) { + wpa_printf(MSG_DEBUG, + "DPP: Unsupported SubjectPublicKeyInfo curve: %s", + buf); + goto fail; + } + + wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen); + + X509_PUBKEY_free(pub); + bi->pubkey = pkey; + return 0; +fail: + X509_PUBKEY_free(pub); + EVP_PKEY_free(pkey); + return -1; +} + + +static struct dpp_bootstrap_info * dpp_parse_uri(const char *uri) +{ + const char *pos = uri; + const char *end; + const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL; + struct dpp_bootstrap_info *bi; + + wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri)); + + if (os_strncmp(pos, "DPP:", 4) != 0) { + wpa_printf(MSG_INFO, "DPP: Not a DPP URI"); + return NULL; + } + pos += 4; + + for (;;) { + end = os_strchr(pos, ';'); + if (!end) + break; + + if (end == pos) { + /* Handle terminating ";;" and ignore unexpected ";" + * for parsing robustness. */ + pos++; + continue; + } + + if (pos[0] == 'C' && pos[1] == ':' && !chan_list) + chan_list = pos + 2; + else if (pos[0] == 'M' && pos[1] == ':' && !mac) + mac = pos + 2; + else if (pos[0] == 'I' && pos[1] == ':' && !info) + info = pos + 2; + else if (pos[0] == 'K' && pos[1] == ':' && !pk) + pk = pos + 2; + else + wpa_hexdump_ascii(MSG_DEBUG, + "DPP: Ignore unrecognized URI parameter", + pos, end - pos); + pos = end + 1; + } + + if (!pk) { + wpa_printf(MSG_INFO, "DPP: URI missing public-key"); + return NULL; + } + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + return NULL; + + if (dpp_clone_uri(bi, uri) < 0 || + dpp_parse_uri_chan_list(bi, chan_list) < 0 || + dpp_parse_uri_mac(bi, mac) < 0 || + dpp_parse_uri_info(bi, info) < 0 || + dpp_parse_uri_pk(bi, pk) < 0) { + dpp_bootstrap_info_free(bi); + bi = NULL; + } + + return bi; +} + + +struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri) +{ + struct dpp_bootstrap_info *bi; + + bi = dpp_parse_uri(uri); + if (bi) + bi->type = DPP_BOOTSTRAP_QR_CODE; + return bi; +} + + +static void dpp_debug_print_key(const char *title, EVP_PKEY *key) +{ + EC_KEY *eckey; + BIO *out; + size_t rlen; + char *txt; + int res; + unsigned char *der = NULL; + int der_len; + const EC_GROUP *group; + const EC_POINT *point; + + out = BIO_new(BIO_s_mem()); + if (!out) + return; + + EVP_PKEY_print_private(out, key, 0, NULL); + rlen = BIO_ctrl_pending(out); + txt = os_malloc(rlen + 1); + if (txt) { + res = BIO_read(out, txt, rlen); + if (res > 0) { + txt[res] = '\0'; + wpa_printf(MSG_DEBUG, "%s: %s", title, txt); + } + os_free(txt); + } + BIO_free(out); + + eckey = EVP_PKEY_get1_EC_KEY(key); + if (!eckey) + return; + + group = EC_KEY_get0_group(eckey); + point = EC_KEY_get0_public_key(eckey); + if (group && point) + dpp_debug_print_point(title, group, point); + + der_len = i2d_ECPrivateKey(eckey, &der); + if (der_len > 0) + wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len); + OPENSSL_free(der); + if (der_len <= 0) { + der = NULL; + der_len = i2d_EC_PUBKEY(eckey, &der); + if (der_len > 0) + wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len); + OPENSSL_free(der); + } + + EC_KEY_free(eckey); +} + + +static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve) +{ + EVP_PKEY_CTX *kctx = NULL; + EC_KEY *ec_params; + EVP_PKEY *params = NULL, *key = NULL; + int nid; + + wpa_printf(MSG_DEBUG, "DPP: Generating a keypair"); + + nid = OBJ_txt2nid(curve->name); + if (nid == NID_undef) { + wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name); + return NULL; + } + + ec_params = EC_KEY_new_by_curve_name(nid); + if (!ec_params) { + wpa_printf(MSG_ERROR, + "DPP: Failed to generate EC_KEY parameters"); + goto fail; + } + EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE); + params = EVP_PKEY_new(); + if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to generate EVP_PKEY parameters"); + goto fail; + } + + kctx = EVP_PKEY_CTX_new(params, NULL); + if (!kctx || + EVP_PKEY_keygen_init(kctx) != 1 || + EVP_PKEY_keygen(kctx, &key) != 1) { + wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key"); + goto fail; + } + + if (wpa_debug_show_keys) + dpp_debug_print_key("Own generated key", key); + + EVP_PKEY_free(params); + EVP_PKEY_CTX_free(kctx); + return key; +fail: + EVP_PKEY_CTX_free(kctx); + EVP_PKEY_free(params); + return NULL; +} + + +static const struct dpp_curve_params * +dpp_get_curve_name(const char *name) +{ + int i; + + for (i = 0; dpp_curves[i].name; i++) { + if (os_strcmp(name, dpp_curves[i].name) == 0 || + (dpp_curves[i].jwk_crv && + os_strcmp(name, dpp_curves[i].jwk_crv) == 0)) + return &dpp_curves[i]; + } + return NULL; +} + + +static const struct dpp_curve_params * +dpp_get_curve_jwk_crv(const char *name) +{ + int i; + + for (i = 0; dpp_curves[i].name; i++) { + if (dpp_curves[i].jwk_crv && + os_strcmp(name, dpp_curves[i].jwk_crv) == 0) + return &dpp_curves[i]; + } + return NULL; +} + + +static EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve, + const u8 *privkey, size_t privkey_len) +{ + EVP_PKEY *pkey; + EC_KEY *eckey; + const EC_GROUP *group; + int nid; + + pkey = EVP_PKEY_new(); + if (!pkey) + return NULL; + eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len); + if (!eckey) { + wpa_printf(MSG_INFO, + "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + EVP_PKEY_free(pkey); + return NULL; + } + group = EC_KEY_get0_group(eckey); + if (!group) { + EC_KEY_free(eckey); + EVP_PKEY_free(pkey); + return NULL; + } + nid = EC_GROUP_get_curve_name(group); + *curve = dpp_get_curve_nid(nid); + if (!*curve) { + wpa_printf(MSG_INFO, + "DPP: Unsupported curve (nid=%d) in pre-assigned key", + nid); + EC_KEY_free(eckey); + EVP_PKEY_free(pkey); + return NULL; + } + + if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) { + EC_KEY_free(eckey); + EVP_PKEY_free(pkey); + return NULL; + } + return pkey; +} + + +typedef struct { + /* AlgorithmIdentifier ecPublicKey with optional parameters present + * as an OID identifying the curve */ + X509_ALGOR *alg; + /* Compressed format public key per ANSI X9.63 */ + ASN1_BIT_STRING *pub_key; +} DPP_BOOTSTRAPPING_KEY; + +ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = { + ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR), + ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING) +} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY); + +IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY); + + +static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key) +{ + unsigned char *der = NULL; + int der_len; + EC_KEY *eckey; + struct wpabuf *ret = NULL; + size_t len; + const EC_GROUP *group; + const EC_POINT *point; + BN_CTX *ctx; + DPP_BOOTSTRAPPING_KEY *bootstrap = NULL; + int nid; + + ctx = BN_CTX_new(); + eckey = EVP_PKEY_get1_EC_KEY(key); + if (!ctx || !eckey) + goto fail; + + group = EC_KEY_get0_group(eckey); + point = EC_KEY_get0_public_key(eckey); + if (!group || !point) + goto fail; + dpp_debug_print_point("DPP: bootstrap public key", group, point); + nid = EC_GROUP_get_curve_name(group); + + bootstrap = DPP_BOOTSTRAPPING_KEY_new(); + if (!bootstrap || + X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC), + V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1) + goto fail; + + len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, + NULL, 0, ctx); + if (len == 0) + goto fail; + + der = OPENSSL_malloc(len); + if (!der) + goto fail; + len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, + der, len, ctx); + + OPENSSL_free(bootstrap->pub_key->data); + bootstrap->pub_key->data = der; + der = NULL; + bootstrap->pub_key->length = len; + /* No unused bits */ + bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07); + bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT; + + der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der); + if (der_len <= 0) { + wpa_printf(MSG_ERROR, + "DDP: Failed to build DER encoded public key"); + goto fail; + } + + ret = wpabuf_alloc_copy(der, der_len); +fail: + DPP_BOOTSTRAPPING_KEY_free(bootstrap); + OPENSSL_free(der); + EC_KEY_free(eckey); + BN_CTX_free(ctx); + return ret; +} + + +int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi) +{ + struct wpabuf *der; + int res; + const u8 *addr[1]; + size_t len[1]; + + der = dpp_bootstrap_key_der(bi->pubkey); + if (!der) + return -1; + wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)", + der); + + addr[0] = wpabuf_head(der); + len[0] = wpabuf_len(der); + res = sha256_vector(1, addr, len, bi->pubkey_hash); + if (res < 0) + wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key"); + else + wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash, + SHA256_MAC_LEN); + wpabuf_free(der); + return res; +} + + +char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve, + const u8 *privkey, size_t privkey_len) +{ + unsigned char *base64 = NULL; + char *pos, *end; + size_t len; + struct wpabuf *der = NULL; + const u8 *addr[1]; + int res; + + if (!curve) { + bi->curve = &dpp_curves[0]; + } else { + bi->curve = dpp_get_curve_name(curve); + if (!bi->curve) { + wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", + curve); + return NULL; + } + } + if (privkey) + bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len); + else + bi->pubkey = dpp_gen_keypair(bi->curve); + if (!bi->pubkey) + goto fail; + bi->own = 1; + + der = dpp_bootstrap_key_der(bi->pubkey); + if (!der) + goto fail; + wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)", + der); + + addr[0] = wpabuf_head(der); + len = wpabuf_len(der); + res = sha256_vector(1, addr, &len, bi->pubkey_hash); + if (res < 0) { + wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash, + SHA256_MAC_LEN); + + base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len); + wpabuf_free(der); + der = NULL; + if (!base64) + goto fail; + pos = (char *) base64; + end = pos + len; + for (;;) { + pos = os_strchr(pos, '\n'); + if (!pos) + break; + os_memmove(pos, pos + 1, end - pos); + } + return (char *) base64; +fail: + os_free(base64); + wpabuf_free(der); + return NULL; +} + + +static int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, + unsigned int hash_len) +{ + u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN]; + const char *info = "first intermediate key"; + int res; + + /* k1 = HKDF(<>, "first intermediate key", M.x) */ + + /* HKDF-Extract(<>, M.x) */ + os_memset(salt, 0, hash_len); + if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)", + prk, hash_len); + + /* HKDF-Expand(PRK, info, L) */ + res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len); + os_memset(prk, 0, hash_len); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)", + k1, hash_len); + return 0; +} + + +static int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, + unsigned int hash_len) +{ + u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN]; + const char *info = "second intermediate key"; + int res; + + /* k2 = HKDF(<>, "second intermediate key", N.x) */ + + /* HKDF-Extract(<>, N.x) */ + os_memset(salt, 0, hash_len); + res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk); + if (res < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)", + prk, hash_len); + + /* HKDF-Expand(PRK, info, L) */ + res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len); + os_memset(prk, 0, hash_len); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)", + k2, hash_len); + return 0; +} + + +static int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke, + unsigned int hash_len) +{ + size_t nonce_len; + u8 nonces[2 * DPP_MAX_NONCE_LEN]; + const char *info_ke = "DPP Key"; + u8 prk[DPP_MAX_HASH_LEN]; + int res; + const u8 *addr[3]; + size_t len[3]; + size_t num_elem = 0; + + if (!auth->Mx_len || !auth->Nx_len) { + wpa_printf(MSG_DEBUG, + "DPP: Mx/Nx not available - cannot derive ke"); + return -1; + } + + /* ke = HKDF(I-nonce | R-nonce, "DPP Key", M.x | N.x [| L.x]) */ + + /* HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */ + nonce_len = auth->curve->nonce_len; + os_memcpy(nonces, auth->i_nonce, nonce_len); + os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len); + addr[num_elem] = auth->Mx; + len[num_elem] = auth->Mx_len; + num_elem++; + addr[num_elem] = auth->Nx; + len[num_elem] = auth->Nx_len; + num_elem++; + if (auth->peer_bi && auth->own_bi) { + if (!auth->Lx_len) { + wpa_printf(MSG_DEBUG, + "DPP: Lx not available - cannot derive ke"); + return -1; + } + addr[num_elem] = auth->Lx; + len[num_elem] = auth->secret_len; + num_elem++; + } + res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len, + num_elem, addr, len, prk); + if (res < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)", + prk, hash_len); + + /* HKDF-Expand(PRK, info, L) */ + res = dpp_hkdf_expand(hash_len, prk, hash_len, info_ke, ke, hash_len); + os_memset(prk, 0, hash_len); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ke = HKDF-Expand(PRK, info, L)", + ke, hash_len); + return 0; +} + + +static void dpp_build_attr_status(struct wpabuf *msg, + enum dpp_status_error status) +{ + wpa_printf(MSG_DEBUG, "DPP: Status %d", status); + wpabuf_put_le16(msg, DPP_ATTR_STATUS); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, status); +} + + +static void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg, + const u8 *hash) +{ + if (hash) { + wpa_printf(MSG_DEBUG, "DPP: R-Bootstrap Key Hash"); + wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH); + wpabuf_put_le16(msg, SHA256_MAC_LEN); + wpabuf_put_data(msg, hash, SHA256_MAC_LEN); + } +} + + +static void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg, + const u8 *hash) +{ + if (hash) { + wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash"); + wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH); + wpabuf_put_le16(msg, SHA256_MAC_LEN); + wpabuf_put_data(msg, hash, SHA256_MAC_LEN); + } +} + + +static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth, + const struct wpabuf *pi, + size_t nonce_len, + const u8 *r_pubkey_hash, + const u8 *i_pubkey_hash, + unsigned int neg_freq) +{ + struct wpabuf *msg; + u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1]; + u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE]; + u8 *pos; + const u8 *addr[2]; + size_t len[2], siv_len, attr_len; + u8 *attr_start, *attr_end; + + /* Build DPP Authentication Request frame attributes */ + attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) + + 4 + sizeof(wrapped_data); + if (neg_freq > 0) + attr_len += 4 + 2; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) + attr_len += 5; +#endif /* CONFIG_TESTING_OPTIONS */ + msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len); + if (!msg) + return NULL; + + attr_start = wpabuf_put(msg, 0); + + /* Responder Bootstrapping Key Hash */ + dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash); + + /* Initiator Bootstrapping Key Hash */ + dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash); + + /* Initiator Protocol Key */ + if (pi) { + wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY); + wpabuf_put_le16(msg, wpabuf_len(pi)); + wpabuf_put_buf(msg, pi); + } + + /* Channel */ + if (neg_freq > 0) { + u8 op_class, channel; + + if (ieee80211_freq_to_channel_ext(neg_freq, 0, 0, &op_class, + &channel) == + NUM_HOSTAPD_MODES) { + wpa_printf(MSG_INFO, + "DPP: Unsupported negotiation frequency request: %d", + neg_freq); + wpabuf_free(msg); + return NULL; + } + wpabuf_put_le16(msg, DPP_ATTR_CHANNEL); + wpabuf_put_le16(msg, 2); + wpabuf_put_u8(msg, op_class); + wpabuf_put_u8(msg, channel); + } + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); + goto skip_wrapped_data; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Wrapped data ({I-nonce, I-capabilities}k1) */ + pos = clear; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce"); + goto skip_i_nonce; + } + if (dpp_test == DPP_TEST_INVALID_I_NONCE_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-nonce"); + WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE); + pos += 2; + WPA_PUT_LE16(pos, nonce_len - 1); + pos += 2; + os_memcpy(pos, auth->i_nonce, nonce_len - 1); + pos += nonce_len - 1; + goto skip_i_nonce; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* I-nonce */ + WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE); + pos += 2; + WPA_PUT_LE16(pos, nonce_len); + pos += 2; + os_memcpy(pos, auth->i_nonce, nonce_len); + pos += nonce_len; + +#ifdef CONFIG_TESTING_OPTIONS +skip_i_nonce: + if (dpp_test == DPP_TEST_NO_I_CAPAB_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-capab"); + goto skip_i_capab; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* I-capabilities */ + WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES); + pos += 2; + WPA_PUT_LE16(pos, 1); + pos += 2; + auth->i_capab = auth->allowed_roles; + *pos++ = auth->i_capab; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_ZERO_I_CAPAB) { + wpa_printf(MSG_INFO, "DPP: TESTING - zero I-capabilities"); + pos[-1] = 0; + } +skip_i_capab: +#endif /* CONFIG_TESTING_OPTIONS */ + + attr_end = wpabuf_put(msg, 0); + + /* OUI, OUI type, Crypto Suite, DPP frame type */ + addr[0] = wpabuf_head_u8(msg) + 2; + len[0] = 3 + 1 + 1 + 1; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + + /* Attributes before Wrapped Data */ + addr[1] = attr_start; + len[1] = attr_end - attr_start; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + siv_len = pos - clear; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len); + if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len, + 2, addr, len, wrapped_data) < 0) { + wpabuf_free(msg); + return NULL; + } + siv_len += AES_BLOCK_SIZE; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, siv_len); + + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, siv_len); + wpabuf_put_data(msg, wrapped_data, siv_len); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); + dpp_build_attr_status(msg, DPP_STATUS_OK); + } +skip_wrapped_data: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Authentication Request frame attributes", msg); + + return msg; +} + + +static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth, + enum dpp_status_error status, + const struct wpabuf *pr, + size_t nonce_len, + const u8 *r_pubkey_hash, + const u8 *i_pubkey_hash, + const u8 *r_nonce, const u8 *i_nonce, + const u8 *wrapped_r_auth, + size_t wrapped_r_auth_len, + const u8 *siv_key) +{ + struct wpabuf *msg; +#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \ + 4 + 4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE + u8 clear[DPP_AUTH_RESP_CLEAR_LEN]; + u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE]; + const u8 *addr[2]; + size_t len[2], siv_len, attr_len; + u8 *attr_start, *attr_end, *pos; + + auth->waiting_auth_conf = 1; + auth->auth_resp_tries = 0; + + /* Build DPP Authentication Response frame attributes */ + attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) + + 4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data); +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) + attr_len += 5; +#endif /* CONFIG_TESTING_OPTIONS */ + msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len); + if (!msg) + return NULL; + + attr_start = wpabuf_put(msg, 0); + + /* DPP Status */ + if (status != 255) + dpp_build_attr_status(msg, status); + + /* Responder Bootstrapping Key Hash */ + dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash); + + /* Initiator Bootstrapping Key Hash (mutual authentication) */ + dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash); + + /* Responder Protocol Key */ + if (pr) { + wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY); + wpabuf_put_le16(msg, wpabuf_len(pr)); + wpabuf_put_buf(msg, pr); + } + + attr_end = wpabuf_put(msg, 0); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); + goto skip_wrapped_data; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */ + pos = clear; + + if (r_nonce) { + /* R-nonce */ + WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE); + pos += 2; + WPA_PUT_LE16(pos, nonce_len); + pos += 2; + os_memcpy(pos, r_nonce, nonce_len); + pos += nonce_len; + } + + if (i_nonce) { + /* I-nonce */ + WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE); + pos += 2; + WPA_PUT_LE16(pos, nonce_len); + pos += 2; + os_memcpy(pos, i_nonce, nonce_len); +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch"); + pos[nonce_len / 2] ^= 0x01; + } +#endif /* CONFIG_TESTING_OPTIONS */ + pos += nonce_len; + } + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab"); + goto skip_r_capab; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* R-capabilities */ + WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES); + pos += 2; + WPA_PUT_LE16(pos, 1); + pos += 2; + auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR : + DPP_CAPAB_ENROLLEE; + *pos++ = auth->r_capab; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_ZERO_R_CAPAB) { + wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities"); + pos[-1] = 0; + } else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - incompatible R-capabilities"); + if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) == + (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) + pos[-1] = 0; + else + pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE : + DPP_CAPAB_CONFIGURATOR; + } +skip_r_capab: +#endif /* CONFIG_TESTING_OPTIONS */ + + if (wrapped_r_auth) { + /* {R-auth}ke */ + WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA); + pos += 2; + WPA_PUT_LE16(pos, wrapped_r_auth_len); + pos += 2; + os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len); + pos += wrapped_r_auth_len; + } + + /* OUI, OUI type, Crypto Suite, DPP frame type */ + addr[0] = wpabuf_head_u8(msg) + 2; + len[0] = 3 + 1 + 1 + 1; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + + /* Attributes before Wrapped Data */ + addr[1] = attr_start; + len[1] = attr_end - attr_start; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + siv_len = pos - clear; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len); + if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len, + 2, addr, len, wrapped_data) < 0) { + wpabuf_free(msg); + return NULL; + } + siv_len += AES_BLOCK_SIZE; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, siv_len); + + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, siv_len); + wpabuf_put_data(msg, wrapped_data, siv_len); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); + dpp_build_attr_status(msg, DPP_STATUS_OK); + } +skip_wrapped_data: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Authentication Response frame attributes", msg); + return msg; +} + + +static int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes, + u16 num_modes, unsigned int freq) +{ + u16 m; + int c, flag; + + if (!own_modes || !num_modes) + return 1; + + for (m = 0; m < num_modes; m++) { + for (c = 0; c < own_modes[m].num_channels; c++) { + if ((unsigned int) own_modes[m].channels[c].freq != + freq) + continue; + flag = own_modes[m].channels[c].flag; + if (!(flag & (HOSTAPD_CHAN_DISABLED | + HOSTAPD_CHAN_NO_IR | + HOSTAPD_CHAN_RADAR))) + return 1; + } + } + + wpa_printf(MSG_DEBUG, "DPP: Peer channel %u MHz not supported", freq); + return 0; +} + + +static int freq_included(const unsigned int freqs[], unsigned int num, + unsigned int freq) +{ + while (num > 0) { + if (freqs[--num] == freq) + return 1; + } + return 0; +} + + +static void freq_to_start(unsigned int freqs[], unsigned int num, + unsigned int freq) +{ + unsigned int i; + + for (i = 0; i < num; i++) { + if (freqs[i] == freq) + break; + } + if (i == 0 || i >= num) + return; + os_memmove(&freqs[1], &freqs[0], i * sizeof(freqs[0])); + freqs[0] = freq; +} + + +static int dpp_channel_intersect(struct dpp_authentication *auth, + struct hostapd_hw_modes *own_modes, + u16 num_modes) +{ + struct dpp_bootstrap_info *peer_bi = auth->peer_bi; + unsigned int i, freq; + + for (i = 0; i < peer_bi->num_freq; i++) { + freq = peer_bi->freq[i]; + if (freq_included(auth->freq, auth->num_freq, freq)) + continue; + if (dpp_channel_ok_init(own_modes, num_modes, freq)) + auth->freq[auth->num_freq++] = freq; + } + if (!auth->num_freq) { + wpa_printf(MSG_INFO, + "DPP: No available channels for initiating DPP Authentication"); + return -1; + } + auth->curr_freq = auth->freq[0]; + return 0; +} + + +static int dpp_channel_local_list(struct dpp_authentication *auth, + struct hostapd_hw_modes *own_modes, + u16 num_modes) +{ + u16 m; + int c, flag; + unsigned int freq; + + auth->num_freq = 0; + + if (!own_modes || !num_modes) { + auth->freq[0] = 2412; + auth->freq[1] = 2437; + auth->freq[2] = 2462; + auth->num_freq = 3; + return 0; + } + + for (m = 0; m < num_modes; m++) { + for (c = 0; c < own_modes[m].num_channels; c++) { + freq = own_modes[m].channels[c].freq; + flag = own_modes[m].channels[c].flag; + if (flag & (HOSTAPD_CHAN_DISABLED | + HOSTAPD_CHAN_NO_IR | + HOSTAPD_CHAN_RADAR)) + continue; + if (freq_included(auth->freq, auth->num_freq, freq)) + continue; + auth->freq[auth->num_freq++] = freq; + if (auth->num_freq == DPP_BOOTSTRAP_MAX_FREQ) { + m = num_modes; + break; + } + } + } + + return auth->num_freq == 0 ? -1 : 0; +} + + +static int dpp_prepare_channel_list(struct dpp_authentication *auth, + struct hostapd_hw_modes *own_modes, + u16 num_modes) +{ + int res; + char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end; + unsigned int i; + + if (auth->peer_bi->num_freq > 0) + res = dpp_channel_intersect(auth, own_modes, num_modes); + else + res = dpp_channel_local_list(auth, own_modes, num_modes); + if (res < 0) + return res; + + /* Prioritize 2.4 GHz channels 6, 1, 11 (in this order) to hit the most + * likely channels first. */ + freq_to_start(auth->freq, auth->num_freq, 2462); + freq_to_start(auth->freq, auth->num_freq, 2412); + freq_to_start(auth->freq, auth->num_freq, 2437); + + auth->freq_idx = 0; + auth->curr_freq = auth->freq[0]; + + pos = freqs; + end = pos + sizeof(freqs); + for (i = 0; i < auth->num_freq; i++) { + res = os_snprintf(pos, end - pos, " %u", auth->freq[i]); + if (os_snprintf_error(end - pos, res)) + break; + pos += res; + } + *pos = '\0'; + wpa_printf(MSG_DEBUG, "DPP: Possible frequencies for initiating:%s", + freqs); + + return 0; +} + + +static int dpp_autogen_bootstrap_key(struct dpp_authentication *auth) +{ + struct dpp_bootstrap_info *bi; + char *pk = NULL; + size_t len; + + if (auth->own_bi) + return 0; /* already generated */ + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + return -1; + bi->type = DPP_BOOTSTRAP_QR_CODE; + pk = dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0); + if (!pk) + goto fail; + + len = 4; /* "DPP:" */ + len += 4 + os_strlen(pk); + bi->uri = os_malloc(len + 1); + if (!bi->uri) + goto fail; + os_snprintf(bi->uri, len + 1, "DPP:K:%s;;", pk); + wpa_printf(MSG_DEBUG, + "DPP: Auto-generated own bootstrapping key info: URI %s", + bi->uri); + + auth->tmp_own_bi = auth->own_bi = bi; + + os_free(pk); + + return 0; +fail: + os_free(pk); + dpp_bootstrap_info_free(bi); + return -1; +} + + +struct dpp_authentication * dpp_auth_init(void *msg_ctx, + struct dpp_bootstrap_info *peer_bi, + struct dpp_bootstrap_info *own_bi, + u8 dpp_allowed_roles, + unsigned int neg_freq, + struct hostapd_hw_modes *own_modes, + u16 num_modes) +{ + struct dpp_authentication *auth; + size_t nonce_len; + EVP_PKEY_CTX *ctx = NULL; + size_t secret_len; + struct wpabuf *pi = NULL; + const u8 *r_pubkey_hash, *i_pubkey_hash; +#ifdef CONFIG_TESTING_OPTIONS + u8 test_hash[SHA256_MAC_LEN]; +#endif /* CONFIG_TESTING_OPTIONS */ + + auth = os_zalloc(sizeof(*auth)); + if (!auth) + return NULL; + auth->msg_ctx = msg_ctx; + auth->initiator = 1; + auth->waiting_auth_resp = 1; + auth->allowed_roles = dpp_allowed_roles; + auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR); + auth->peer_bi = peer_bi; + auth->own_bi = own_bi; + auth->curve = peer_bi->curve; + + if (dpp_autogen_bootstrap_key(auth) < 0 || + dpp_prepare_channel_list(auth, own_modes, num_modes) < 0) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_nonce_override_len > 0) { + wpa_printf(MSG_INFO, "DPP: TESTING - override I-nonce"); + nonce_len = dpp_nonce_override_len; + os_memcpy(auth->i_nonce, dpp_nonce_override, nonce_len); + } else { + nonce_len = auth->curve->nonce_len; + if (random_get_bytes(auth->i_nonce, nonce_len)) { + wpa_printf(MSG_ERROR, + "DPP: Failed to generate I-nonce"); + goto fail; + } + } +#else /* CONFIG_TESTING_OPTIONS */ + nonce_len = auth->curve->nonce_len; + if (random_get_bytes(auth->i_nonce, nonce_len)) { + wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce"); + goto fail; + } +#endif /* CONFIG_TESTING_OPTIONS */ + wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_protocol_key_override_len) { + const struct dpp_curve_params *tmp_curve; + + wpa_printf(MSG_INFO, + "DPP: TESTING - override protocol key"); + auth->own_protocol_key = dpp_set_keypair( + &tmp_curve, dpp_protocol_key_override, + dpp_protocol_key_override_len); + } else { + auth->own_protocol_key = dpp_gen_keypair(auth->curve); + } +#else /* CONFIG_TESTING_OPTIONS */ + auth->own_protocol_key = dpp_gen_keypair(auth->curve); +#endif /* CONFIG_TESTING_OPTIONS */ + if (!auth->own_protocol_key) + goto fail; + + pi = dpp_get_pubkey_point(auth->own_protocol_key, 0); + if (!pi) + goto fail; + + /* ECDH: M = pI * BR */ + ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, auth->peer_bi->pubkey) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || + secret_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + auth->secret_len = secret_len; + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)", + auth->Mx, auth->secret_len); + auth->Mx_len = auth->secret_len; + + if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1, + auth->curve->hash_len) < 0) + goto fail; + + r_pubkey_hash = auth->peer_bi->pubkey_hash; + i_pubkey_hash = auth->own_bi->pubkey_hash; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); + r_pubkey_hash = NULL; + } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid R-Bootstrap Key Hash"); + os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + r_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); + i_pubkey_hash = NULL; + } else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid I-Bootstrap Key Hash"); + os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + i_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key"); + wpabuf_free(pi); + pi = NULL; + } else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key"); + wpabuf_free(pi); + pi = wpabuf_alloc(2 * auth->curve->prime_len); + if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0) + goto fail; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash, + i_pubkey_hash, neg_freq); + if (!auth->req_msg) + goto fail; + +out: + wpabuf_free(pi); + EVP_PKEY_CTX_free(ctx); + return auth; +fail: + dpp_auth_deinit(auth); + auth = NULL; + goto out; +} + + +struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth, + const char *json) +{ + size_t nonce_len; + size_t json_len, clear_len; + struct wpabuf *clear = NULL, *msg = NULL; + u8 *wrapped; + size_t attr_len; + + wpa_printf(MSG_DEBUG, "DPP: Build configuration request"); + + nonce_len = auth->curve->nonce_len; + if (random_get_bytes(auth->e_nonce, nonce_len)) { + wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", auth->e_nonce, nonce_len); + json_len = os_strlen(json); + wpa_hexdump_ascii(MSG_DEBUG, "DPP: configAttr JSON", json, json_len); + + /* { E-nonce, configAttrib }ke */ + clear_len = 4 + nonce_len + 4 + json_len; + clear = wpabuf_alloc(clear_len); + attr_len = 4 + clear_len + AES_BLOCK_SIZE; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) + attr_len += 5; +#endif /* CONFIG_TESTING_OPTIONS */ + msg = wpabuf_alloc(attr_len); + if (!clear || !msg) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce"); + goto skip_e_nonce; + } + if (dpp_test == DPP_TEST_INVALID_E_NONCE_CONF_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid E-nonce"); + wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); + wpabuf_put_le16(clear, nonce_len - 1); + wpabuf_put_data(clear, auth->e_nonce, nonce_len - 1); + goto skip_e_nonce; + } + if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); + goto skip_wrapped_data; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* E-nonce */ + wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); + wpabuf_put_le16(clear, nonce_len); + wpabuf_put_data(clear, auth->e_nonce, nonce_len); + +#ifdef CONFIG_TESTING_OPTIONS +skip_e_nonce: + if (dpp_test == DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no configAttrib"); + goto skip_conf_attr_obj; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* configAttrib */ + wpabuf_put_le16(clear, DPP_ATTR_CONFIG_ATTR_OBJ); + wpabuf_put_le16(clear, json_len); + wpabuf_put_data(clear, json, json_len); + +#ifdef CONFIG_TESTING_OPTIONS +skip_conf_attr_obj: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + + /* No AES-SIV AD */ + wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); + if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, + wpabuf_head(clear), wpabuf_len(clear), + 0, NULL, NULL, wrapped) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); + dpp_build_attr_status(msg, DPP_STATUS_OK); + } +skip_wrapped_data: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Configuration Request frame attributes", msg); + wpabuf_free(clear); + return msg; + +fail: + wpabuf_free(clear); + wpabuf_free(msg); + return NULL; +} + + +static void dpp_auth_success(struct dpp_authentication *auth) +{ + wpa_printf(MSG_DEBUG, + "DPP: Authentication success - clear temporary keys"); + os_memset(auth->Mx, 0, sizeof(auth->Mx)); + auth->Mx_len = 0; + os_memset(auth->Nx, 0, sizeof(auth->Nx)); + auth->Nx_len = 0; + os_memset(auth->Lx, 0, sizeof(auth->Lx)); + auth->Lx_len = 0; + os_memset(auth->k1, 0, sizeof(auth->k1)); + os_memset(auth->k2, 0, sizeof(auth->k2)); + + auth->auth_success = 1; +} + + +static int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth) +{ + struct wpabuf *pix, *prx, *bix, *brx; + const u8 *addr[7]; + size_t len[7]; + size_t i, num_elem = 0; + size_t nonce_len; + u8 zero = 0; + int res = -1; + + /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */ + nonce_len = auth->curve->nonce_len; + + if (auth->initiator) { + pix = dpp_get_pubkey_point(auth->own_protocol_key, 0); + prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0); + if (auth->own_bi) + bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); + else + bix = NULL; + brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); + } else { + pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0); + prx = dpp_get_pubkey_point(auth->own_protocol_key, 0); + if (auth->peer_bi) + bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); + else + bix = NULL; + brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); + } + if (!pix || !prx || !brx) + goto fail; + + addr[num_elem] = auth->i_nonce; + len[num_elem] = nonce_len; + num_elem++; + + addr[num_elem] = auth->r_nonce; + len[num_elem] = nonce_len; + num_elem++; + + addr[num_elem] = wpabuf_head(pix); + len[num_elem] = wpabuf_len(pix) / 2; + num_elem++; + + addr[num_elem] = wpabuf_head(prx); + len[num_elem] = wpabuf_len(prx) / 2; + num_elem++; + + if (bix) { + addr[num_elem] = wpabuf_head(bix); + len[num_elem] = wpabuf_len(bix) / 2; + num_elem++; + } + + addr[num_elem] = wpabuf_head(brx); + len[num_elem] = wpabuf_len(brx) / 2; + num_elem++; + + addr[num_elem] = &zero; + len[num_elem] = 1; + num_elem++; + + wpa_printf(MSG_DEBUG, "DPP: R-auth hash components"); + for (i = 0; i < num_elem; i++) + wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]); + res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth); + if (res == 0) + wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth, + auth->curve->hash_len); +fail: + wpabuf_free(pix); + wpabuf_free(prx); + wpabuf_free(bix); + wpabuf_free(brx); + return res; +} + + +static int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth) +{ + struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL; + const u8 *addr[7]; + size_t len[7]; + size_t i, num_elem = 0; + size_t nonce_len; + u8 one = 1; + int res = -1; + + /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */ + nonce_len = auth->curve->nonce_len; + + if (auth->initiator) { + pix = dpp_get_pubkey_point(auth->own_protocol_key, 0); + prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0); + if (auth->own_bi) + bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); + else + bix = NULL; + if (!auth->peer_bi) + goto fail; + brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); + } else { + pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0); + prx = dpp_get_pubkey_point(auth->own_protocol_key, 0); + if (auth->peer_bi) + bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); + else + bix = NULL; + if (!auth->own_bi) + goto fail; + brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); + } + if (!pix || !prx || !brx) + goto fail; + + addr[num_elem] = auth->r_nonce; + len[num_elem] = nonce_len; + num_elem++; + + addr[num_elem] = auth->i_nonce; + len[num_elem] = nonce_len; + num_elem++; + + addr[num_elem] = wpabuf_head(prx); + len[num_elem] = wpabuf_len(prx) / 2; + num_elem++; + + addr[num_elem] = wpabuf_head(pix); + len[num_elem] = wpabuf_len(pix) / 2; + num_elem++; + + addr[num_elem] = wpabuf_head(brx); + len[num_elem] = wpabuf_len(brx) / 2; + num_elem++; + + if (bix) { + addr[num_elem] = wpabuf_head(bix); + len[num_elem] = wpabuf_len(bix) / 2; + num_elem++; + } + + addr[num_elem] = &one; + len[num_elem] = 1; + num_elem++; + + wpa_printf(MSG_DEBUG, "DPP: I-auth hash components"); + for (i = 0; i < num_elem; i++) + wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]); + res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth); + if (res == 0) + wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth, + auth->curve->hash_len); +fail: + wpabuf_free(pix); + wpabuf_free(prx); + wpabuf_free(bix); + wpabuf_free(brx); + return res; +} + + +static int dpp_auth_derive_l_responder(struct dpp_authentication *auth) +{ + const EC_GROUP *group; + EC_POINT *l = NULL; + EC_KEY *BI = NULL, *bR = NULL, *pR = NULL; + const EC_POINT *BI_point; + BN_CTX *bnctx; + BIGNUM *lx, *sum, *q; + const BIGNUM *bR_bn, *pR_bn; + int ret = -1; + + /* L = ((bR + pR) modulo q) * BI */ + + bnctx = BN_CTX_new(); + sum = BN_new(); + q = BN_new(); + lx = BN_new(); + if (!bnctx || !sum || !q || !lx) + goto fail; + BI = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey); + if (!BI) + goto fail; + BI_point = EC_KEY_get0_public_key(BI); + group = EC_KEY_get0_group(BI); + if (!group) + goto fail; + + bR = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey); + pR = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key); + if (!bR || !pR) + goto fail; + bR_bn = EC_KEY_get0_private_key(bR); + pR_bn = EC_KEY_get0_private_key(pR); + if (!bR_bn || !pR_bn) + goto fail; + if (EC_GROUP_get_order(group, q, bnctx) != 1 || + BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1) + goto fail; + l = EC_POINT_new(group); + if (!l || + EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 || + EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL, + bnctx) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0) + goto fail; + wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len); + auth->Lx_len = auth->secret_len; + ret = 0; +fail: + EC_POINT_clear_free(l); + EC_KEY_free(BI); + EC_KEY_free(bR); + EC_KEY_free(pR); + BN_clear_free(lx); + BN_clear_free(sum); + BN_free(q); + BN_CTX_free(bnctx); + return ret; +} + + +static int dpp_auth_derive_l_initiator(struct dpp_authentication *auth) +{ + const EC_GROUP *group; + EC_POINT *l = NULL, *sum = NULL; + EC_KEY *bI = NULL, *BR = NULL, *PR = NULL; + const EC_POINT *BR_point, *PR_point; + BN_CTX *bnctx; + BIGNUM *lx; + const BIGNUM *bI_bn; + int ret = -1; + + /* L = bI * (BR + PR) */ + + bnctx = BN_CTX_new(); + lx = BN_new(); + if (!bnctx || !lx) + goto fail; + BR = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey); + PR = EVP_PKEY_get1_EC_KEY(auth->peer_protocol_key); + if (!BR || !PR) + goto fail; + BR_point = EC_KEY_get0_public_key(BR); + PR_point = EC_KEY_get0_public_key(PR); + + bI = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey); + if (!bI) + goto fail; + group = EC_KEY_get0_group(bI); + bI_bn = EC_KEY_get0_private_key(bI); + if (!group || !bI_bn) + goto fail; + sum = EC_POINT_new(group); + l = EC_POINT_new(group); + if (!sum || !l || + EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 || + EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 || + EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL, + bnctx) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0) + goto fail; + wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len); + auth->Lx_len = auth->secret_len; + ret = 0; +fail: + EC_POINT_clear_free(l); + EC_POINT_clear_free(sum); + EC_KEY_free(bI); + EC_KEY_free(BR); + EC_KEY_free(PR); + BN_clear_free(lx); + BN_CTX_free(bnctx); + return ret; +} + + +static int dpp_auth_build_resp_ok(struct dpp_authentication *auth) +{ + size_t nonce_len; + EVP_PKEY_CTX *ctx = NULL; + size_t secret_len; + struct wpabuf *msg, *pr = NULL; + u8 r_auth[4 + DPP_MAX_HASH_LEN]; + u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth; + size_t wrapped_r_auth_len; + int ret = -1; + const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce; + enum dpp_status_error status = DPP_STATUS_OK; +#ifdef CONFIG_TESTING_OPTIONS + u8 test_hash[SHA256_MAC_LEN]; +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response"); + if (!auth->own_bi) + return -1; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_nonce_override_len > 0) { + wpa_printf(MSG_INFO, "DPP: TESTING - override R-nonce"); + nonce_len = dpp_nonce_override_len; + os_memcpy(auth->r_nonce, dpp_nonce_override, nonce_len); + } else { + nonce_len = auth->curve->nonce_len; + if (random_get_bytes(auth->r_nonce, nonce_len)) { + wpa_printf(MSG_ERROR, + "DPP: Failed to generate R-nonce"); + goto fail; + } + } +#else /* CONFIG_TESTING_OPTIONS */ + nonce_len = auth->curve->nonce_len; + if (random_get_bytes(auth->r_nonce, nonce_len)) { + wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce"); + goto fail; + } +#endif /* CONFIG_TESTING_OPTIONS */ + wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_protocol_key_override_len) { + const struct dpp_curve_params *tmp_curve; + + wpa_printf(MSG_INFO, + "DPP: TESTING - override protocol key"); + auth->own_protocol_key = dpp_set_keypair( + &tmp_curve, dpp_protocol_key_override, + dpp_protocol_key_override_len); + } else { + auth->own_protocol_key = dpp_gen_keypair(auth->curve); + } +#else /* CONFIG_TESTING_OPTIONS */ + auth->own_protocol_key = dpp_gen_keypair(auth->curve); +#endif /* CONFIG_TESTING_OPTIONS */ + if (!auth->own_protocol_key) + goto fail; + + pr = dpp_get_pubkey_point(auth->own_protocol_key, 0); + if (!pr) + goto fail; + + /* ECDH: N = pR * PI */ + ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, auth->peer_protocol_key) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || + secret_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", + auth->Nx, auth->secret_len); + auth->Nx_len = auth->secret_len; + + if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2, + auth->curve->hash_len) < 0) + goto fail; + + if (auth->own_bi && auth->peer_bi) { + /* Mutual authentication */ + if (dpp_auth_derive_l_responder(auth) < 0) + goto fail; + } + + if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0) + goto fail; + + /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */ + WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG); + WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len); + if (dpp_gen_r_auth(auth, r_auth + 4) < 0) + goto fail; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch"); + r_auth[4 + auth->curve->hash_len / 2] ^= 0x01; + } +#endif /* CONFIG_TESTING_OPTIONS */ + if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, + r_auth, 4 + auth->curve->hash_len, + 0, NULL, NULL, wrapped_r_auth) < 0) + goto fail; + wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE; + wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke", + wrapped_r_auth, wrapped_r_auth_len); + w_r_auth = wrapped_r_auth; + + r_pubkey_hash = auth->own_bi->pubkey_hash; + if (auth->peer_bi) + i_pubkey_hash = auth->peer_bi->pubkey_hash; + else + i_pubkey_hash = NULL; + + i_nonce = auth->i_nonce; + r_nonce = auth->r_nonce; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); + r_pubkey_hash = NULL; + } else if (dpp_test == + DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid R-Bootstrap Key Hash"); + os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + r_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); + i_pubkey_hash = NULL; + } else if (dpp_test == + DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid I-Bootstrap Key Hash"); + if (i_pubkey_hash) + os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); + else + os_memset(test_hash, 0, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + i_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key"); + wpabuf_free(pr); + pr = NULL; + } else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key"); + wpabuf_free(pr); + pr = wpabuf_alloc(2 * auth->curve->prime_len); + if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0) + goto fail; + } else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth"); + w_r_auth = NULL; + wrapped_r_auth_len = 0; + } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); + status = 255; + } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); + status = 254; + } else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce"); + r_nonce = NULL; + } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce"); + i_nonce = NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + msg = dpp_auth_build_resp(auth, status, pr, nonce_len, + r_pubkey_hash, i_pubkey_hash, + r_nonce, i_nonce, + w_r_auth, wrapped_r_auth_len, + auth->k2); + if (!msg) + goto fail; + wpabuf_free(auth->resp_msg); + auth->resp_msg = msg; + ret = 0; +fail: + wpabuf_free(pr); + return ret; +} + + +static int dpp_auth_build_resp_status(struct dpp_authentication *auth, + enum dpp_status_error status) +{ + struct wpabuf *msg; + const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce; +#ifdef CONFIG_TESTING_OPTIONS + u8 test_hash[SHA256_MAC_LEN]; +#endif /* CONFIG_TESTING_OPTIONS */ + + if (!auth->own_bi) + return -1; + wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response"); + + r_pubkey_hash = auth->own_bi->pubkey_hash; + if (auth->peer_bi) + i_pubkey_hash = auth->peer_bi->pubkey_hash; + else + i_pubkey_hash = NULL; + + i_nonce = auth->i_nonce; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); + r_pubkey_hash = NULL; + } else if (dpp_test == + DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid R-Bootstrap Key Hash"); + os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + r_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); + i_pubkey_hash = NULL; + } else if (dpp_test == + DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid I-Bootstrap Key Hash"); + if (i_pubkey_hash) + os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); + else + os_memset(test_hash, 0, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + i_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); + status = 255; + } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce"); + i_nonce = NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len, + r_pubkey_hash, i_pubkey_hash, + NULL, i_nonce, NULL, 0, auth->k1); + if (!msg) + return -1; + wpabuf_free(auth->resp_msg); + auth->resp_msg = msg; + return 0; +} + + +struct dpp_authentication * +dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual, + struct dpp_bootstrap_info *peer_bi, + struct dpp_bootstrap_info *own_bi, + unsigned int freq, const u8 *hdr, const u8 *attr_start, + size_t attr_len) +{ + EVP_PKEY *pi = NULL; + EVP_PKEY_CTX *ctx = NULL; + size_t secret_len; + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap, + *channel; + u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len, + i_bootstrap_len, channel_len; + struct dpp_authentication *auth = NULL; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at Authentication Request"); + return NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Missing or invalid required Wrapped Data attribute"); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data", + wrapped_data, wrapped_data_len); + attr_len = wrapped_data - 4 - attr_start; + + auth = os_zalloc(sizeof(*auth)); + if (!auth) + goto fail; + auth->msg_ctx = msg_ctx; + auth->peer_bi = peer_bi; + auth->own_bi = own_bi; + auth->curve = own_bi->curve; + auth->curr_freq = freq; + + channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL, + &channel_len); + if (channel) { + int neg_freq; + + if (channel_len < 2) { + dpp_auth_fail(auth, "Too short Channel attribute"); + goto fail; + } + + neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]); + wpa_printf(MSG_DEBUG, + "DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d", + channel[0], channel[1], neg_freq); + if (neg_freq < 0) { + dpp_auth_fail(auth, + "Unsupported Channel attribute value"); + goto fail; + } + + if (auth->curr_freq != (unsigned int) neg_freq) { + wpa_printf(MSG_DEBUG, + "DPP: Changing negotiation channel from %u MHz to %u MHz", + freq, neg_freq); + auth->curr_freq = neg_freq; + } + } + + i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY, + &i_proto_len); + if (!i_proto) { + dpp_auth_fail(auth, + "Missing required Initiator Protocol Key attribute"); + goto fail; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key", + i_proto, i_proto_len); + + /* M = bR * PI */ + pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len); + if (!pi) { + dpp_auth_fail(auth, "Invalid Initiator Protocol Key"); + goto fail; + } + dpp_debug_print_key("Peer (Initiator) Protocol Key", pi); + + ctx = EVP_PKEY_CTX_new(own_bi->pubkey, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pi) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || + secret_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + dpp_auth_fail(auth, "Failed to derive ECDH shared secret"); + goto fail; + } + auth->secret_len = secret_len; + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)", + auth->Mx, auth->secret_len); + auth->Mx_len = auth->secret_len; + + if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1, + auth->curve->hash_len) < 0) + goto fail; + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + addr[1] = attr_start; + len[1] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + if (aes_siv_decrypt(auth->k1, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE, + &i_nonce_len); + if (!i_nonce || i_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, "Missing or invalid I-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len); + os_memcpy(auth->i_nonce, i_nonce, i_nonce_len); + + i_capab = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_I_CAPABILITIES, + &i_capab_len); + if (!i_capab || i_capab_len < 1) { + dpp_auth_fail(auth, "Missing or invalid I-capabilities"); + goto fail; + } + auth->i_capab = i_capab[0]; + wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab); + + bin_clear_free(unwrapped, unwrapped_len); + unwrapped = NULL; + + switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) { + case DPP_CAPAB_ENROLLEE: + if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) { + wpa_printf(MSG_DEBUG, + "DPP: Local policy does not allow Configurator role"); + goto not_compatible; + } + wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator"); + auth->configurator = 1; + break; + case DPP_CAPAB_CONFIGURATOR: + if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) { + wpa_printf(MSG_DEBUG, + "DPP: Local policy does not allow Enrollee role"); + goto not_compatible; + } + wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee"); + auth->configurator = 0; + break; + case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE: + if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) { + wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee"); + auth->configurator = 0; + } else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) { + wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator"); + auth->configurator = 1; + } else { + wpa_printf(MSG_DEBUG, + "DPP: Local policy does not allow Configurator/Enrollee role"); + goto not_compatible; + } + break; + default: + wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities"); + wpa_msg(auth->msg_ctx, MSG_INFO, + DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x", + auth->i_capab & DPP_CAPAB_ROLE_MASK); + goto fail; + } + + auth->peer_protocol_key = pi; + pi = NULL; + if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) { + char hex[SHA256_MAC_LEN * 2 + 1]; + + wpa_printf(MSG_DEBUG, + "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time"); + if (dpp_auth_build_resp_status(auth, + DPP_STATUS_RESPONSE_PENDING) < 0) + goto fail; + i_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_I_BOOTSTRAP_KEY_HASH, + &i_bootstrap_len); + if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) { + auth->response_pending = 1; + os_memcpy(auth->waiting_pubkey_hash, + i_bootstrap, i_bootstrap_len); + wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap, + i_bootstrap_len); + } else { + hex[0] = '\0'; + } + + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE + "%s", hex); + return auth; + } + if (dpp_auth_build_resp_ok(auth) < 0) + goto fail; + + return auth; + +not_compatible: + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE + "i-capab=0x%02x", auth->i_capab); + if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) + auth->configurator = 1; + else + auth->configurator = 0; + auth->peer_protocol_key = pi; + pi = NULL; + if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0) + goto fail; + + auth->remove_on_tx_status = 1; + return auth; +fail: + bin_clear_free(unwrapped, unwrapped_len); + EVP_PKEY_free(pi); + EVP_PKEY_CTX_free(ctx); + dpp_auth_deinit(auth); + return NULL; +} + + +int dpp_notify_new_qr_code(struct dpp_authentication *auth, + struct dpp_bootstrap_info *peer_bi) +{ + if (!auth || !auth->response_pending || + os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) + return 0; + + wpa_printf(MSG_DEBUG, + "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with " + MACSTR, MAC2STR(auth->peer_mac_addr)); + auth->peer_bi = peer_bi; + + if (dpp_auth_build_resp_ok(auth) < 0) + return -1; + + return 1; +} + + +static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth, + enum dpp_status_error status) +{ + struct wpabuf *msg; + u8 i_auth[4 + DPP_MAX_HASH_LEN]; + size_t i_auth_len; + u8 r_nonce[4 + DPP_MAX_NONCE_LEN]; + size_t r_nonce_len; + const u8 *addr[2]; + size_t len[2], attr_len; + u8 *wrapped_i_auth; + u8 *wrapped_r_nonce; + u8 *attr_start, *attr_end; + const u8 *r_pubkey_hash, *i_pubkey_hash; +#ifdef CONFIG_TESTING_OPTIONS + u8 test_hash[SHA256_MAC_LEN]; +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation"); + + i_auth_len = 4 + auth->curve->hash_len; + r_nonce_len = 4 + auth->curve->nonce_len; + /* Build DPP Authentication Confirmation frame attributes */ + attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) + + 4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) + attr_len += 5; +#endif /* CONFIG_TESTING_OPTIONS */ + msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len); + if (!msg) + goto fail; + + attr_start = wpabuf_put(msg, 0); + + r_pubkey_hash = auth->peer_bi->pubkey_hash; + if (auth->own_bi) + i_pubkey_hash = auth->own_bi->pubkey_hash; + else + i_pubkey_hash = NULL; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); + goto skip_status; + } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); + status = 254; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* DPP Status */ + dpp_build_attr_status(msg, status); + +#ifdef CONFIG_TESTING_OPTIONS +skip_status: + if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); + r_pubkey_hash = NULL; + } else if (dpp_test == + DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid R-Bootstrap Key Hash"); + os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + r_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); + i_pubkey_hash = NULL; + } else if (dpp_test == + DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid I-Bootstrap Key Hash"); + if (i_pubkey_hash) + os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); + else + os_memset(test_hash, 0, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + i_pubkey_hash = test_hash; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Responder Bootstrapping Key Hash */ + dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash); + + /* Initiator Bootstrapping Key Hash (mutual authentication) */ + dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF) + goto skip_wrapped_data; + if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF) + i_auth_len = 0; +#endif /* CONFIG_TESTING_OPTIONS */ + + attr_end = wpabuf_put(msg, 0); + + /* OUI, OUI type, Crypto Suite, DPP frame type */ + addr[0] = wpabuf_head_u8(msg) + 2; + len[0] = 3 + 1 + 1 + 1; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + + /* Attributes before Wrapped Data */ + addr[1] = attr_start; + len[1] = attr_end - attr_start; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + if (status == DPP_STATUS_OK) { + /* I-auth wrapped with ke */ + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE); + wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF) + goto skip_i_auth; +#endif /* CONFIG_TESTING_OPTIONS */ + + /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] + * 1) */ + WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG); + WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len); + if (dpp_gen_i_auth(auth, i_auth + 4) < 0) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) { + wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch"); + i_auth[4 + auth->curve->hash_len / 2] ^= 0x01; + } +skip_i_auth: +#endif /* CONFIG_TESTING_OPTIONS */ + if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, + i_auth, i_auth_len, + 2, addr, len, wrapped_i_auth) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke", + wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE); + } else { + /* R-nonce wrapped with k2 */ + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE); + wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE); + + WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE); + WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len); + os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len); + + if (aes_siv_encrypt(auth->k2, auth->curve->hash_len, + r_nonce, r_nonce_len, + 2, addr, len, wrapped_r_nonce) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2", + wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE); + } + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) { + wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); + dpp_build_attr_status(msg, DPP_STATUS_OK); + } +skip_wrapped_data: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Authentication Confirmation frame attributes", + msg); + if (status == DPP_STATUS_OK) + dpp_auth_success(auth); + + return msg; + +fail: + wpabuf_free(msg); + return NULL; +} + + +static void +dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr, + const u8 *attr_start, size_t attr_len, + const u8 *wrapped_data, u16 wrapped_data_len, + enum dpp_status_error status) +{ + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + const u8 *i_nonce, *r_capab; + u16 i_nonce_len, r_capab_len; + + if (status == DPP_STATUS_NOT_COMPATIBLE) { + wpa_printf(MSG_DEBUG, + "DPP: Responder reported incompatible roles"); + } else if (status == DPP_STATUS_RESPONSE_PENDING) { + wpa_printf(MSG_DEBUG, + "DPP: Responder reported more time needed"); + } else { + wpa_printf(MSG_DEBUG, + "DPP: Responder reported failure (status %d)", + status); + dpp_auth_fail(auth, "Responder reported failure"); + return; + } + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + addr[1] = attr_start; + len[1] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + if (aes_siv_decrypt(auth->k1, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE, + &i_nonce_len); + if (!i_nonce || i_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, "Missing or invalid I-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len); + if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) { + dpp_auth_fail(auth, "I-nonce mismatch"); + goto fail; + } + + r_capab = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_R_CAPABILITIES, + &r_capab_len); + if (!r_capab || r_capab_len < 1) { + dpp_auth_fail(auth, "Missing or invalid R-capabilities"); + goto fail; + } + auth->r_capab = r_capab[0]; + wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab); + if (status == DPP_STATUS_NOT_COMPATIBLE) { + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE + "r-capab=0x%02x", auth->r_capab); + } else if (status == DPP_STATUS_RESPONSE_PENDING) { + u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK; + + if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) || + (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) { + wpa_msg(auth->msg_ctx, MSG_INFO, + DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x", + role); + } else { + wpa_printf(MSG_DEBUG, + "DPP: Continue waiting for full DPP Authentication Response"); + wpa_msg(auth->msg_ctx, MSG_INFO, + DPP_EVENT_RESPONSE_PENDING "%s", + auth->tmp_own_bi ? auth->tmp_own_bi->uri : ""); + } + } +fail: + bin_clear_free(unwrapped, unwrapped_len); +} + + +struct wpabuf * +dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr, + const u8 *attr_start, size_t attr_len) +{ + EVP_PKEY *pr; + EVP_PKEY_CTX *ctx = NULL; + size_t secret_len; + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL, *unwrapped2 = NULL; + size_t unwrapped_len = 0, unwrapped2_len = 0; + const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto, + *r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth; + u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len, + r_proto_len, r_nonce_len, i_nonce_len, r_capab_len, + wrapped2_len, r_auth_len; + u8 r_auth2[DPP_MAX_HASH_LEN]; + u8 role; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at Authentication Response"); + return NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (!auth->initiator || !auth->peer_bi) { + dpp_auth_fail(auth, "Unexpected Authentication Response"); + return NULL; + } + + auth->waiting_auth_resp = 0; + + wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + dpp_auth_fail(auth, + "Missing or invalid required Wrapped Data attribute"); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", + wrapped_data, wrapped_data_len); + + attr_len = wrapped_data - 4 - attr_start; + + r_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_R_BOOTSTRAP_KEY_HASH, + &r_bootstrap_len); + if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { + dpp_auth_fail(auth, + "Missing or invalid required Responder Bootstrapping Key Hash attribute"); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash", + r_bootstrap, r_bootstrap_len); + if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) { + dpp_auth_fail(auth, + "Unexpected Responder Bootstrapping Key Hash value"); + wpa_hexdump(MSG_DEBUG, + "DPP: Expected Responder Bootstrapping Key Hash", + auth->peer_bi->pubkey_hash, SHA256_MAC_LEN); + return NULL; + } + + i_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_I_BOOTSTRAP_KEY_HASH, + &i_bootstrap_len); + if (i_bootstrap) { + if (i_bootstrap_len != SHA256_MAC_LEN) { + dpp_auth_fail(auth, + "Invalid Initiator Bootstrapping Key Hash attribute"); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, + "DPP: Initiator Bootstrapping Key Hash", + i_bootstrap, i_bootstrap_len); + if (!auth->own_bi || + os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) { + dpp_auth_fail(auth, + "Initiator Bootstrapping Key Hash attribute did not match"); + return NULL; + } + } else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) { + /* PKEX bootstrapping mandates use of mutual authentication */ + dpp_auth_fail(auth, + "Missing Initiator Bootstrapping Key Hash attribute"); + return NULL; + } + + status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS, + &status_len); + if (!status || status_len < 1) { + dpp_auth_fail(auth, + "Missing or invalid required DPP Status attribute"); + return NULL; + } + wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); + auth->auth_resp_status = status[0]; + if (status[0] != DPP_STATUS_OK) { + dpp_auth_resp_rx_status(auth, hdr, attr_start, + attr_len, wrapped_data, + wrapped_data_len, status[0]); + return NULL; + } + + if (!i_bootstrap && auth->own_bi) { + wpa_printf(MSG_DEBUG, + "DPP: Responder decided not to use mutual authentication"); + auth->own_bi = NULL; + } + + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_DIRECTION "mutual=%d", + auth->own_bi != NULL); + + r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY, + &r_proto_len); + if (!r_proto) { + dpp_auth_fail(auth, + "Missing required Responder Protocol Key attribute"); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key", + r_proto, r_proto_len); + + /* N = pI * PR */ + pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len); + if (!pr) { + dpp_auth_fail(auth, "Invalid Responder Protocol Key"); + return NULL; + } + dpp_debug_print_key("Peer (Responder) Protocol Key", pr); + + ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pr) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || + secret_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + dpp_auth_fail(auth, "Failed to derive ECDH shared secret"); + goto fail; + } + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + auth->peer_protocol_key = pr; + pr = NULL; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", + auth->Nx, auth->secret_len); + auth->Nx_len = auth->secret_len; + + if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2, + auth->curve->hash_len) < 0) + goto fail; + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + addr[1] = attr_start; + len[1] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + if (aes_siv_decrypt(auth->k2, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE, + &r_nonce_len); + if (!r_nonce || r_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len); + os_memcpy(auth->r_nonce, r_nonce, r_nonce_len); + + i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE, + &i_nonce_len); + if (!i_nonce || i_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, "Missing or invalid I-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len); + if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) { + dpp_auth_fail(auth, "I-nonce mismatch"); + goto fail; + } + + if (auth->own_bi) { + /* Mutual authentication */ + if (dpp_auth_derive_l_initiator(auth) < 0) + goto fail; + } + + r_capab = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_R_CAPABILITIES, + &r_capab_len); + if (!r_capab || r_capab_len < 1) { + dpp_auth_fail(auth, "Missing or invalid R-capabilities"); + goto fail; + } + auth->r_capab = r_capab[0]; + wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab); + role = auth->r_capab & DPP_CAPAB_ROLE_MASK; + if ((auth->allowed_roles == + (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) && + (role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) { + /* Peer selected its role, so move from "either role" to the + * role that is compatible with peer's selection. */ + auth->configurator = role == DPP_CAPAB_ENROLLEE; + wpa_printf(MSG_DEBUG, "DPP: Acting as %s", + auth->configurator ? "Configurator" : "Enrollee"); + } else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) || + (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) { + wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection"); + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Unexpected role in R-capabilities 0x%02x", + role); + if (role != DPP_CAPAB_ENROLLEE && + role != DPP_CAPAB_CONFIGURATOR) + goto fail; + bin_clear_free(unwrapped, unwrapped_len); + auth->remove_on_tx_status = 1; + return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE); + } + + wrapped2 = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_WRAPPED_DATA, &wrapped2_len); + if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) { + dpp_auth_fail(auth, + "Missing or invalid Secondary Wrapped Data"); + goto fail; + } + + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped2, wrapped2_len); + + if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0) + goto fail; + + unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE; + unwrapped2 = os_malloc(unwrapped2_len); + if (!unwrapped2) + goto fail; + if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, + wrapped2, wrapped2_len, + 0, NULL, NULL, unwrapped2) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped2, unwrapped2_len); + + if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) { + dpp_auth_fail(auth, + "Invalid attribute in secondary unwrapped data"); + goto fail; + } + + r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG, + &r_auth_len); + if (!r_auth || r_auth_len != auth->curve->hash_len) { + dpp_auth_fail(auth, + "Missing or invalid Responder Authenticating Tag"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag", + r_auth, r_auth_len); + /* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */ + if (dpp_gen_r_auth(auth, r_auth2) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag", + r_auth2, r_auth_len); + if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) { + dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag"); + bin_clear_free(unwrapped, unwrapped_len); + bin_clear_free(unwrapped2, unwrapped2_len); + auth->remove_on_tx_status = 1; + return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE); + } + + bin_clear_free(unwrapped, unwrapped_len); + bin_clear_free(unwrapped2, unwrapped2_len); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) { + wpa_printf(MSG_INFO, + "DPP: TESTING - Authentication Response in place of Confirm"); + if (dpp_auth_build_resp_ok(auth) < 0) + return NULL; + return wpabuf_dup(auth->resp_msg); + } +#endif /* CONFIG_TESTING_OPTIONS */ + + return dpp_auth_build_conf(auth, DPP_STATUS_OK); + +fail: + bin_clear_free(unwrapped, unwrapped_len); + bin_clear_free(unwrapped2, unwrapped2_len); + EVP_PKEY_free(pr); + EVP_PKEY_CTX_free(ctx); + return NULL; +} + + +static int dpp_auth_conf_rx_failure(struct dpp_authentication *auth, + const u8 *hdr, + const u8 *attr_start, size_t attr_len, + const u8 *wrapped_data, + u16 wrapped_data_len, + enum dpp_status_error status) +{ + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + const u8 *r_nonce; + u16 r_nonce_len; + + /* Authentication Confirm failure cases are expected to include + * {R-nonce}k2 in the Wrapped Data attribute. */ + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + addr[1] = attr_start; + len[1] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) { + dpp_auth_fail(auth, "Authentication failed"); + goto fail; + } + if (aes_siv_decrypt(auth->k2, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE, + &r_nonce_len); + if (!r_nonce || r_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce"); + goto fail; + } + if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) { + wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce", + r_nonce, r_nonce_len); + wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce", + auth->r_nonce, r_nonce_len); + dpp_auth_fail(auth, "R-nonce mismatch"); + goto fail; + } + + if (status == DPP_STATUS_NOT_COMPATIBLE) + dpp_auth_fail(auth, "Peer reported incompatible R-capab role"); + else if (status == DPP_STATUS_AUTH_FAILURE) + dpp_auth_fail(auth, "Peer reported authentication failure)"); + +fail: + bin_clear_free(unwrapped, unwrapped_len); + return -1; +} + + +int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr, + const u8 *attr_start, size_t attr_len) +{ + const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth; + u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len, + i_auth_len; + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + u8 i_auth2[DPP_MAX_HASH_LEN]; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at Authentication Confirm"); + return -1; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (auth->initiator || !auth->own_bi) { + dpp_auth_fail(auth, "Unexpected Authentication Confirm"); + return -1; + } + + auth->waiting_auth_conf = 0; + + wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + dpp_auth_fail(auth, + "Missing or invalid required Wrapped Data attribute"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", + wrapped_data, wrapped_data_len); + + attr_len = wrapped_data - 4 - attr_start; + + r_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_R_BOOTSTRAP_KEY_HASH, + &r_bootstrap_len); + if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { + dpp_auth_fail(auth, + "Missing or invalid required Responder Bootstrapping Key Hash attribute"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash", + r_bootstrap, r_bootstrap_len); + if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) { + wpa_hexdump(MSG_DEBUG, + "DPP: Expected Responder Bootstrapping Key Hash", + auth->peer_bi->pubkey_hash, SHA256_MAC_LEN); + dpp_auth_fail(auth, + "Responder Bootstrapping Key Hash mismatch"); + return -1; + } + + i_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_I_BOOTSTRAP_KEY_HASH, + &i_bootstrap_len); + if (i_bootstrap) { + if (i_bootstrap_len != SHA256_MAC_LEN) { + dpp_auth_fail(auth, + "Invalid Initiator Bootstrapping Key Hash attribute"); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, + "DPP: Initiator Bootstrapping Key Hash", + i_bootstrap, i_bootstrap_len); + if (!auth->peer_bi || + os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) { + dpp_auth_fail(auth, + "Initiator Bootstrapping Key Hash mismatch"); + return -1; + } + } else if (auth->peer_bi) { + /* Mutual authentication and peer did not include its + * Bootstrapping Key Hash attribute. */ + dpp_auth_fail(auth, + "Missing Initiator Bootstrapping Key Hash attribute"); + return -1; + } + + status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS, + &status_len); + if (!status || status_len < 1) { + dpp_auth_fail(auth, + "Missing or invalid required DPP Status attribute"); + return -1; + } + wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); + if (status[0] == DPP_STATUS_NOT_COMPATIBLE || + status[0] == DPP_STATUS_AUTH_FAILURE) + return dpp_auth_conf_rx_failure(auth, hdr, attr_start, + attr_len, wrapped_data, + wrapped_data_len, status[0]); + + if (status[0] != DPP_STATUS_OK) { + dpp_auth_fail(auth, "Authentication failed"); + return -1; + } + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + addr[1] = attr_start; + len[1] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + return -1; + if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG, + &i_auth_len); + if (!i_auth || i_auth_len != auth->curve->hash_len) { + dpp_auth_fail(auth, + "Missing or invalid Initiator Authenticating Tag"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag", + i_auth, i_auth_len); + /* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */ + if (dpp_gen_i_auth(auth, i_auth2) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag", + i_auth2, i_auth_len); + if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) { + dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag"); + goto fail; + } + + bin_clear_free(unwrapped, unwrapped_len); + dpp_auth_success(auth); + return 0; +fail: + bin_clear_free(unwrapped, unwrapped_len); + return -1; +} + + +void dpp_configuration_free(struct dpp_configuration *conf) +{ + if (!conf) + return; + str_clear_free(conf->passphrase); + os_free(conf->group_id); + bin_clear_free(conf, sizeof(*conf)); +} + + +void dpp_auth_deinit(struct dpp_authentication *auth) +{ + if (!auth) + return; + dpp_configuration_free(auth->conf_ap); + dpp_configuration_free(auth->conf_sta); + EVP_PKEY_free(auth->own_protocol_key); + EVP_PKEY_free(auth->peer_protocol_key); + wpabuf_free(auth->req_msg); + wpabuf_free(auth->resp_msg); + wpabuf_free(auth->conf_req); + os_free(auth->connector); + wpabuf_free(auth->net_access_key); + wpabuf_free(auth->c_sign_key); + dpp_bootstrap_info_free(auth->tmp_own_bi); +#ifdef CONFIG_TESTING_OPTIONS + os_free(auth->config_obj_override); + os_free(auth->discovery_override); + os_free(auth->groups_override); +#endif /* CONFIG_TESTING_OPTIONS */ + bin_clear_free(auth, sizeof(*auth)); +} + + +static struct wpabuf * +dpp_build_conf_start(struct dpp_authentication *auth, + struct dpp_configuration *conf, size_t tailroom) +{ + struct wpabuf *buf; + char ssid[6 * sizeof(conf->ssid) + 1]; + +#ifdef CONFIG_TESTING_OPTIONS + if (auth->discovery_override) + tailroom += os_strlen(auth->discovery_override); +#endif /* CONFIG_TESTING_OPTIONS */ + + buf = wpabuf_alloc(200 + tailroom); + if (!buf) + return NULL; + wpabuf_put_str(buf, "{\"wi-fi_tech\":\"infra\",\"discovery\":"); +#ifdef CONFIG_TESTING_OPTIONS + if (auth->discovery_override) { + wpa_printf(MSG_DEBUG, "DPP: TESTING - discovery override: '%s'", + auth->discovery_override); + wpabuf_put_str(buf, auth->discovery_override); + wpabuf_put_u8(buf, ','); + return buf; + } +#endif /* CONFIG_TESTING_OPTIONS */ + wpabuf_put_str(buf, "{\"ssid\":\""); + json_escape_string(ssid, sizeof(ssid), + (const char *) conf->ssid, conf->ssid_len); + wpabuf_put_str(buf, ssid); + wpabuf_put_str(buf, "\"},"); + + return buf; +} + + +static int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key, + const char *kid, const struct dpp_curve_params *curve) +{ + struct wpabuf *pub; + const u8 *pos; + char *x = NULL, *y = NULL; + int ret = -1; + + pub = dpp_get_pubkey_point(key, 0); + if (!pub) + goto fail; + pos = wpabuf_head(pub); + x = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0); + pos += curve->prime_len; + y = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0); + if (!x || !y) + goto fail; + + wpabuf_put_str(buf, "\""); + wpabuf_put_str(buf, name); + wpabuf_put_str(buf, "\":{\"kty\":\"EC\",\"crv\":\""); + wpabuf_put_str(buf, curve->jwk_crv); + wpabuf_put_str(buf, "\",\"x\":\""); + wpabuf_put_str(buf, x); + wpabuf_put_str(buf, "\",\"y\":\""); + wpabuf_put_str(buf, y); + if (kid) { + wpabuf_put_str(buf, "\",\"kid\":\""); + wpabuf_put_str(buf, kid); + } + wpabuf_put_str(buf, "\"}"); + ret = 0; +fail: + wpabuf_free(pub); + os_free(x); + os_free(y); + return ret; +} + + +static struct wpabuf * +dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap, + struct dpp_configuration *conf) +{ + struct wpabuf *buf = NULL; + char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL; + size_t tailroom; + const struct dpp_curve_params *curve; + char jws_prot_hdr[100]; + size_t signed1_len, signed2_len, signed3_len; + struct wpabuf *dppcon = NULL; + unsigned char *signature = NULL; + const unsigned char *p; + size_t signature_len; + EVP_MD_CTX *md_ctx = NULL; + ECDSA_SIG *sig = NULL; + char *dot = "."; + const EVP_MD *sign_md; + const BIGNUM *r, *s; + size_t extra_len = 1000; + + if (!auth->conf) { + wpa_printf(MSG_INFO, + "DPP: No configurator specified - cannot generate DPP config object"); + goto fail; + } + curve = auth->conf->curve; + if (curve->hash_len == SHA256_MAC_LEN) { + sign_md = EVP_sha256(); + } else if (curve->hash_len == SHA384_MAC_LEN) { + sign_md = EVP_sha384(); + } else if (curve->hash_len == SHA512_MAC_LEN) { + sign_md = EVP_sha512(); + } else { + wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm"); + goto fail; + } + +#ifdef CONFIG_TESTING_OPTIONS + if (auth->groups_override) + extra_len += os_strlen(auth->groups_override); +#endif /* CONFIG_TESTING_OPTIONS */ + + if (conf->group_id) + extra_len += os_strlen(conf->group_id); + + /* Connector (JSON dppCon object) */ + dppcon = wpabuf_alloc(extra_len + 2 * auth->curve->prime_len * 4 / 3); + if (!dppcon) + goto fail; +#ifdef CONFIG_TESTING_OPTIONS + if (auth->groups_override) { + wpabuf_put_u8(dppcon, '{'); + if (auth->groups_override) { + wpa_printf(MSG_DEBUG, + "DPP: TESTING - groups override: '%s'", + auth->groups_override); + wpabuf_put_str(dppcon, "\"groups\":"); + wpabuf_put_str(dppcon, auth->groups_override); + wpabuf_put_u8(dppcon, ','); + } + goto skip_groups; + } +#endif /* CONFIG_TESTING_OPTIONS */ + wpabuf_printf(dppcon, "{\"groups\":[{\"groupId\":\"%s\",", + conf->group_id ? conf->group_id : "*"); + wpabuf_printf(dppcon, "\"netRole\":\"%s\"}],", ap ? "ap" : "sta"); +#ifdef CONFIG_TESTING_OPTIONS +skip_groups: +#endif /* CONFIG_TESTING_OPTIONS */ + if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL, + auth->curve) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK"); + goto fail; + } + if (conf->netaccesskey_expiry) { + struct os_tm tm; + + if (os_gmtime(conf->netaccesskey_expiry, &tm) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to generate expiry string"); + goto fail; + } + wpabuf_printf(dppcon, + ",\"expiry\":\"%04u-%02u-%02uT%02u:%02u:%02uZ\"", + tm.year, tm.month, tm.day, + tm.hour, tm.min, tm.sec); + } + wpabuf_put_u8(dppcon, '}'); + wpa_printf(MSG_DEBUG, "DPP: dppCon: %s", + (const char *) wpabuf_head(dppcon)); + + os_snprintf(jws_prot_hdr, sizeof(jws_prot_hdr), + "{\"typ\":\"dppCon\",\"kid\":\"%s\",\"alg\":\"%s\"}", + auth->conf->kid, curve->jws_alg); + signed1 = (char *) base64_url_encode((unsigned char *) jws_prot_hdr, + os_strlen(jws_prot_hdr), + &signed1_len, 0); + signed2 = (char *) base64_url_encode(wpabuf_head(dppcon), + wpabuf_len(dppcon), + &signed2_len, 0); + if (!signed1 || !signed2) + goto fail; + + md_ctx = EVP_MD_CTX_create(); + if (!md_ctx) + goto fail; + + ERR_clear_error(); + if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL, + auth->conf->csign) != 1) { + wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 || + EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 || + EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) { + wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) { + wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + signature = os_malloc(signature_len); + if (!signature) + goto fail; + if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) { + wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)", + signature, signature_len); + /* Convert to raw coordinates r,s */ + p = signature; + sig = d2i_ECDSA_SIG(NULL, &p, signature_len); + if (!sig) + goto fail; + ECDSA_SIG_get0(sig, &r, &s); + if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 || + dpp_bn2bin_pad(s, signature + curve->prime_len, + curve->prime_len) < 0) + goto fail; + signature_len = 2 * curve->prime_len; + wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)", + signature, signature_len); + signed3 = (char *) base64_url_encode(signature, signature_len, + &signed3_len, 0); + if (!signed3) + goto fail; + + tailroom = 1000; + tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid); + tailroom += signed1_len + signed2_len + signed3_len; + buf = dpp_build_conf_start(auth, conf, tailroom); + if (!buf) + goto fail; + + wpabuf_put_str(buf, "\"cred\":{\"akm\":\"dpp\",\"signedConnector\":\""); + wpabuf_put_str(buf, signed1); + wpabuf_put_u8(buf, '.'); + wpabuf_put_str(buf, signed2); + wpabuf_put_u8(buf, '.'); + wpabuf_put_str(buf, signed3); + wpabuf_put_str(buf, "\","); + if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid, + curve) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK"); + goto fail; + } + + wpabuf_put_str(buf, "}}"); + + wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object", + wpabuf_head(buf), wpabuf_len(buf)); + +out: + EVP_MD_CTX_destroy(md_ctx); + ECDSA_SIG_free(sig); + os_free(signed1); + os_free(signed2); + os_free(signed3); + os_free(signature); + wpabuf_free(dppcon); + return buf; +fail: + wpa_printf(MSG_DEBUG, "DPP: Failed to build configuration object"); + wpabuf_free(buf); + buf = NULL; + goto out; +} + + +static struct wpabuf * +dpp_build_conf_obj_legacy(struct dpp_authentication *auth, int ap, + struct dpp_configuration *conf) +{ + struct wpabuf *buf; + + buf = dpp_build_conf_start(auth, conf, 1000); + if (!buf) + return NULL; + + wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(conf->akm)); + if (conf->passphrase) { + char pass[63 * 6 + 1]; + + if (os_strlen(conf->passphrase) > 63) { + wpabuf_free(buf); + return NULL; + } + + json_escape_string(pass, sizeof(pass), conf->passphrase, + os_strlen(conf->passphrase)); + wpabuf_put_str(buf, "\"pass\":\""); + wpabuf_put_str(buf, pass); + wpabuf_put_str(buf, "\""); + } else { + char psk[2 * sizeof(conf->psk) + 1]; + + wpa_snprintf_hex(psk, sizeof(psk), + conf->psk, sizeof(conf->psk)); + wpabuf_put_str(buf, "\"psk_hex\":\""); + wpabuf_put_str(buf, psk); + wpabuf_put_str(buf, "\""); + } + wpabuf_put_str(buf, "}}"); + + wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)", + wpabuf_head(buf), wpabuf_len(buf)); + + return buf; +} + + +static struct wpabuf * +dpp_build_conf_obj(struct dpp_authentication *auth, int ap) +{ + struct dpp_configuration *conf; + +#ifdef CONFIG_TESTING_OPTIONS + if (auth->config_obj_override) { + wpa_printf(MSG_DEBUG, "DPP: Testing - Config Object override"); + return wpabuf_alloc_copy(auth->config_obj_override, + os_strlen(auth->config_obj_override)); + } +#endif /* CONFIG_TESTING_OPTIONS */ + + conf = ap ? auth->conf_ap : auth->conf_sta; + if (!conf) { + wpa_printf(MSG_DEBUG, + "DPP: No configuration available for Enrollee(%s) - reject configuration request", + ap ? "ap" : "sta"); + return NULL; + } + + if (conf->akm == DPP_AKM_DPP) + return dpp_build_conf_obj_dpp(auth, ap, conf); + return dpp_build_conf_obj_legacy(auth, ap, conf); +} + + +static struct wpabuf * +dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce, + u16 e_nonce_len, int ap) +{ + struct wpabuf *conf; + size_t clear_len, attr_len; + struct wpabuf *clear = NULL, *msg = NULL; + u8 *wrapped; + const u8 *addr[1]; + size_t len[1]; + enum dpp_status_error status; + + conf = dpp_build_conf_obj(auth, ap); + if (conf) { + wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON", + wpabuf_head(conf), wpabuf_len(conf)); + } + status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE; + + /* { E-nonce, configurationObject}ke */ + clear_len = 4 + e_nonce_len; + if (conf) + clear_len += 4 + wpabuf_len(conf); + clear = wpabuf_alloc(clear_len); + attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) + attr_len += 5; +#endif /* CONFIG_TESTING_OPTIONS */ + msg = wpabuf_alloc(attr_len); + if (!clear || !msg) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce"); + goto skip_e_nonce; + } + if (dpp_test == DPP_TEST_E_NONCE_MISMATCH_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - E-nonce mismatch"); + wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); + wpabuf_put_le16(clear, e_nonce_len); + wpabuf_put_data(clear, e_nonce, e_nonce_len - 1); + wpabuf_put_u8(clear, e_nonce[e_nonce_len - 1] ^ 0x01); + goto skip_e_nonce; + } + if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); + goto skip_wrapped_data; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* E-nonce */ + wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); + wpabuf_put_le16(clear, e_nonce_len); + wpabuf_put_data(clear, e_nonce, e_nonce_len); + +#ifdef CONFIG_TESTING_OPTIONS +skip_e_nonce: + if (dpp_test == DPP_TEST_NO_CONFIG_OBJ_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - Config Object"); + goto skip_config_obj; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (conf) { + wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ); + wpabuf_put_le16(clear, wpabuf_len(conf)); + wpabuf_put_buf(clear, conf); + } + +#ifdef CONFIG_TESTING_OPTIONS +skip_config_obj: + if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - Status"); + goto skip_status; + } + if (dpp_test == DPP_TEST_INVALID_STATUS_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); + status = 255; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* DPP Status */ + dpp_build_attr_status(msg, status); + +#ifdef CONFIG_TESTING_OPTIONS +skip_status: +#endif /* CONFIG_TESTING_OPTIONS */ + + addr[0] = wpabuf_head(msg); + len[0] = wpabuf_len(msg); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); + + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + + wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); + if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, + wpabuf_head(clear), wpabuf_len(clear), + 1, addr, len, wrapped) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); + dpp_build_attr_status(msg, DPP_STATUS_OK); + } +skip_wrapped_data: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Configuration Response attributes", msg); +out: + wpabuf_free(conf); + wpabuf_free(clear); + + return msg; +fail: + wpabuf_free(msg); + msg = NULL; + goto out; +} + + +struct wpabuf * +dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, + size_t attr_len) +{ + const u8 *wrapped_data, *e_nonce, *config_attr; + u16 wrapped_data_len, e_nonce_len, config_attr_len; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + struct wpabuf *resp = NULL; + struct json_token *root = NULL, *token; + int ap; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at Config Request"); + return NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (dpp_check_attrs(attr_start, attr_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in config request"); + return NULL; + } + + wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + dpp_auth_fail(auth, + "Missing or invalid required Wrapped Data attribute"); + return NULL; + } + + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + return NULL; + if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 0, NULL, NULL, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + e_nonce = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_ENROLLEE_NONCE, + &e_nonce_len); + if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, + "Missing or invalid Enrollee Nonce attribute"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); + + config_attr = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_CONFIG_ATTR_OBJ, + &config_attr_len); + if (!config_attr) { + dpp_auth_fail(auth, + "Missing or invalid Config Attributes attribute"); + goto fail; + } + wpa_hexdump_ascii(MSG_DEBUG, "DPP: Config Attributes", + config_attr, config_attr_len); + + root = json_parse((const char *) config_attr, config_attr_len); + if (!root) { + dpp_auth_fail(auth, "Could not parse Config Attributes"); + goto fail; + } + + token = json_get_member(root, "name"); + if (!token || token->type != JSON_STRING) { + dpp_auth_fail(auth, "No Config Attributes - name"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: Enrollee name = '%s'", token->string); + + token = json_get_member(root, "wi-fi_tech"); + if (!token || token->type != JSON_STRING) { + dpp_auth_fail(auth, "No Config Attributes - wi-fi_tech"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: wi-fi_tech = '%s'", token->string); + if (os_strcmp(token->string, "infra") != 0) { + wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech '%s'", + token->string); + dpp_auth_fail(auth, "Unsupported wi-fi_tech"); + goto fail; + } + + token = json_get_member(root, "netRole"); + if (!token || token->type != JSON_STRING) { + dpp_auth_fail(auth, "No Config Attributes - netRole"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string); + if (os_strcmp(token->string, "sta") == 0) { + ap = 0; + } else if (os_strcmp(token->string, "ap") == 0) { + ap = 1; + } else { + wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'", + token->string); + dpp_auth_fail(auth, "Unsupported netRole"); + goto fail; + } + + resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, ap); + +fail: + json_free(root); + os_free(unwrapped); + return resp; +} + + +static struct wpabuf * +dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve, + const u8 *prot_hdr, u16 prot_hdr_len, + const EVP_MD **ret_md) +{ + struct json_token *root, *token; + struct wpabuf *kid = NULL; + + root = json_parse((const char *) prot_hdr, prot_hdr_len); + if (!root) { + wpa_printf(MSG_DEBUG, + "DPP: JSON parsing failed for JWS Protected Header"); + goto fail; + } + + if (root->type != JSON_OBJECT) { + wpa_printf(MSG_DEBUG, + "DPP: JWS Protected Header root is not an object"); + goto fail; + } + + token = json_get_member(root, "typ"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, "DPP: No typ string value found"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s", + token->string); + if (os_strcmp(token->string, "dppCon") != 0) { + wpa_printf(MSG_DEBUG, + "DPP: Unsupported JWS Protected Header typ=%s", + token->string); + goto fail; + } + + token = json_get_member(root, "alg"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, "DPP: No alg string value found"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s", + token->string); + if (os_strcmp(token->string, curve->jws_alg) != 0) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)", + token->string, curve->jws_alg); + goto fail; + } + if (os_strcmp(token->string, "ES256") == 0 || + os_strcmp(token->string, "BS256") == 0) + *ret_md = EVP_sha256(); + else if (os_strcmp(token->string, "ES384") == 0 || + os_strcmp(token->string, "BS384") == 0) + *ret_md = EVP_sha384(); + else if (os_strcmp(token->string, "ES512") == 0 || + os_strcmp(token->string, "BS512") == 0) + *ret_md = EVP_sha512(); + else + *ret_md = NULL; + if (!*ret_md) { + wpa_printf(MSG_DEBUG, + "DPP: Unsupported JWS Protected Header alg=%s", + token->string); + goto fail; + } + + kid = json_get_member_base64url(root, "kid"); + if (!kid) { + wpa_printf(MSG_DEBUG, "DPP: No kid string value found"); + goto fail; + } + wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)", + kid); + +fail: + json_free(root); + return kid; +} + + +static int dpp_parse_cred_legacy(struct dpp_authentication *auth, + struct json_token *cred) +{ + struct json_token *pass, *psk_hex; + + wpa_printf(MSG_DEBUG, "DPP: Legacy akm=psk credential"); + + pass = json_get_member(cred, "pass"); + psk_hex = json_get_member(cred, "psk_hex"); + + if (pass && pass->type == JSON_STRING) { + size_t len = os_strlen(pass->string); + + wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Legacy passphrase", + pass->string, len); + if (len < 8 || len > 63) + return -1; + os_strlcpy(auth->passphrase, pass->string, + sizeof(auth->passphrase)); + } else if (psk_hex && psk_hex->type == JSON_STRING) { + if (auth->akm == DPP_AKM_SAE) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected psk_hex with akm=sae"); + return -1; + } + if (os_strlen(psk_hex->string) != PMK_LEN * 2 || + hexstr2bin(psk_hex->string, auth->psk, PMK_LEN) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding"); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "DPP: Legacy PSK", + auth->psk, PMK_LEN); + auth->psk_set = 1; + } else { + wpa_printf(MSG_DEBUG, "DPP: No pass or psk_hex strings found"); + return -1; + } + + if ((auth->akm == DPP_AKM_SAE || auth->akm == DPP_AKM_PSK_SAE) && + !auth->passphrase[0]) { + wpa_printf(MSG_DEBUG, "DPP: No pass for sae found"); + return -1; + } + + return 0; +} + + +static EVP_PKEY * dpp_parse_jwk(struct json_token *jwk, + const struct dpp_curve_params **key_curve) +{ + struct json_token *token; + const struct dpp_curve_params *curve; + struct wpabuf *x = NULL, *y = NULL; + EC_GROUP *group; + EVP_PKEY *pkey = NULL; + + token = json_get_member(jwk, "kty"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, "DPP: No kty in JWK"); + goto fail; + } + if (os_strcmp(token->string, "EC") != 0) { + wpa_printf(MSG_DEBUG, "DPP: Unexpected JWK kty '%s'", + token->string); + goto fail; + } + + token = json_get_member(jwk, "crv"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, "DPP: No crv in JWK"); + goto fail; + } + curve = dpp_get_curve_jwk_crv(token->string); + if (!curve) { + wpa_printf(MSG_DEBUG, "DPP: Unsupported JWK crv '%s'", + token->string); + goto fail; + } + + x = json_get_member_base64url(jwk, "x"); + if (!x) { + wpa_printf(MSG_DEBUG, "DPP: No x in JWK"); + goto fail; + } + wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK x", x); + if (wpabuf_len(x) != curve->prime_len) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected JWK x length %u (expected %u for curve %s)", + (unsigned int) wpabuf_len(x), + (unsigned int) curve->prime_len, curve->name); + goto fail; + } + + y = json_get_member_base64url(jwk, "y"); + if (!y) { + wpa_printf(MSG_DEBUG, "DPP: No y in JWK"); + goto fail; + } + wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK y", y); + if (wpabuf_len(y) != curve->prime_len) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected JWK y length %u (expected %u for curve %s)", + (unsigned int) wpabuf_len(y), + (unsigned int) curve->prime_len, curve->name); + goto fail; + } + + group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name)); + if (!group) { + wpa_printf(MSG_DEBUG, "DPP: Could not prepare group for JWK"); + goto fail; + } + + pkey = dpp_set_pubkey_point_group(group, wpabuf_head(x), wpabuf_head(y), + wpabuf_len(x)); + *key_curve = curve; + +fail: + wpabuf_free(x); + wpabuf_free(y); + + return pkey; +} + + +int dpp_key_expired(const char *timestamp, os_time_t *expiry) +{ + struct os_time now; + unsigned int year, month, day, hour, min, sec; + os_time_t utime; + const char *pos; + + /* ISO 8601 date and time: + * <date>T<time> + * YYYY-MM-DDTHH:MM:SSZ + * YYYY-MM-DDTHH:MM:SS+03:00 + */ + if (os_strlen(timestamp) < 19) { + wpa_printf(MSG_DEBUG, + "DPP: Too short timestamp - assume expired key"); + return 1; + } + if (sscanf(timestamp, "%04u-%02u-%02uT%02u:%02u:%02u", + &year, &month, &day, &hour, &min, &sec) != 6) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to parse expiration day - assume expired key"); + return 1; + } + + if (os_mktime(year, month, day, hour, min, sec, &utime) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Invalid date/time information - assume expired key"); + return 1; + } + + pos = timestamp + 19; + if (*pos == 'Z' || *pos == '\0') { + /* In UTC - no need to adjust */ + } else if (*pos == '-' || *pos == '+') { + int items; + + /* Adjust local time to UTC */ + items = sscanf(pos + 1, "%02u:%02u", &hour, &min); + if (items < 1) { + wpa_printf(MSG_DEBUG, + "DPP: Invalid time zone designator (%s) - assume expired key", + pos); + return 1; + } + if (*pos == '-') + utime += 3600 * hour; + if (*pos == '+') + utime -= 3600 * hour; + if (items > 1) { + if (*pos == '-') + utime += 60 * min; + if (*pos == '+') + utime -= 60 * min; + } + } else { + wpa_printf(MSG_DEBUG, + "DPP: Invalid time zone designator (%s) - assume expired key", + pos); + return 1; + } + if (expiry) + *expiry = utime; + + if (os_get_time(&now) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Cannot get current time - assume expired key"); + return 1; + } + + if (now.sec > utime) { + wpa_printf(MSG_DEBUG, "DPP: Key has expired (%lu < %lu)", + utime, now.sec); + return 1; + } + + return 0; +} + + +static int dpp_parse_connector(struct dpp_authentication *auth, + const unsigned char *payload, + u16 payload_len) +{ + struct json_token *root, *groups, *netkey, *token; + int ret = -1; + EVP_PKEY *key = NULL; + const struct dpp_curve_params *curve; + unsigned int rules = 0; + + root = json_parse((const char *) payload, payload_len); + if (!root) { + wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed"); + goto fail; + } + + groups = json_get_member(root, "groups"); + if (!groups || groups->type != JSON_ARRAY) { + wpa_printf(MSG_DEBUG, "DPP: No groups array found"); + goto skip_groups; + } + for (token = groups->child; token; token = token->sibling) { + struct json_token *id, *role; + + id = json_get_member(token, "groupId"); + if (!id || id->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, "DPP: Missing groupId string"); + goto fail; + } + + role = json_get_member(token, "netRole"); + if (!role || role->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, "DPP: Missing netRole string"); + goto fail; + } + wpa_printf(MSG_DEBUG, + "DPP: connector group: groupId='%s' netRole='%s'", + id->string, role->string); + rules++; + } +skip_groups: + + if (!rules) { + wpa_printf(MSG_DEBUG, + "DPP: Connector includes no groups"); + goto fail; + } + + token = json_get_member(root, "expiry"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, + "DPP: No expiry string found - connector does not expire"); + } else { + wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string); + if (dpp_key_expired(token->string, + &auth->net_access_key_expiry)) { + wpa_printf(MSG_DEBUG, + "DPP: Connector (netAccessKey) has expired"); + goto fail; + } + } + + netkey = json_get_member(root, "netAccessKey"); + if (!netkey || netkey->type != JSON_OBJECT) { + wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found"); + goto fail; + } + + key = dpp_parse_jwk(netkey, &curve); + if (!key) + goto fail; + dpp_debug_print_key("DPP: Received netAccessKey", key); + + if (EVP_PKEY_cmp(key, auth->own_protocol_key) != 1) { + wpa_printf(MSG_DEBUG, + "DPP: netAccessKey in connector does not match own protocol key"); +#ifdef CONFIG_TESTING_OPTIONS + if (auth->ignore_netaccesskey_mismatch) { + wpa_printf(MSG_DEBUG, + "DPP: TESTING - skip netAccessKey mismatch"); + } else { + goto fail; + } +#else /* CONFIG_TESTING_OPTIONS */ + goto fail; +#endif /* CONFIG_TESTING_OPTIONS */ + } + + ret = 0; +fail: + EVP_PKEY_free(key); + json_free(root); + return ret; +} + + +static int dpp_check_pubkey_match(EVP_PKEY *pub, struct wpabuf *r_hash) +{ + struct wpabuf *uncomp; + int res; + u8 hash[SHA256_MAC_LEN]; + const u8 *addr[1]; + size_t len[1]; + + if (wpabuf_len(r_hash) != SHA256_MAC_LEN) + return -1; + uncomp = dpp_get_pubkey_point(pub, 1); + if (!uncomp) + return -1; + addr[0] = wpabuf_head(uncomp); + len[0] = wpabuf_len(uncomp); + wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed public key", + addr[0], len[0]); + res = sha256_vector(1, addr, len, hash); + wpabuf_free(uncomp); + if (res < 0) + return -1; + if (os_memcmp(hash, wpabuf_head(r_hash), SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "DPP: Received hash value does not match calculated public key hash value"); + wpa_hexdump(MSG_DEBUG, "DPP: Calculated hash", + hash, SHA256_MAC_LEN); + return -1; + } + return 0; +} + + +static void dpp_copy_csign(struct dpp_authentication *auth, EVP_PKEY *csign) +{ + unsigned char *der = NULL; + int der_len; + + der_len = i2d_PUBKEY(csign, &der); + if (der_len <= 0) + return; + wpabuf_free(auth->c_sign_key); + auth->c_sign_key = wpabuf_alloc_copy(der, der_len); + OPENSSL_free(der); +} + + +static void dpp_copy_netaccesskey(struct dpp_authentication *auth) +{ + unsigned char *der = NULL; + int der_len; + EC_KEY *eckey; + + eckey = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key); + if (!eckey) + return; + + der_len = i2d_ECPrivateKey(eckey, &der); + if (der_len <= 0) { + EC_KEY_free(eckey); + return; + } + wpabuf_free(auth->net_access_key); + auth->net_access_key = wpabuf_alloc_copy(der, der_len); + OPENSSL_free(der); + EC_KEY_free(eckey); +} + + +struct dpp_signed_connector_info { + unsigned char *payload; + size_t payload_len; +}; + +static enum dpp_status_error +dpp_process_signed_connector(struct dpp_signed_connector_info *info, + EVP_PKEY *csign_pub, const char *connector) +{ + enum dpp_status_error ret = 255; + const char *pos, *end, *signed_start, *signed_end; + struct wpabuf *kid = NULL; + unsigned char *prot_hdr = NULL, *signature = NULL; + size_t prot_hdr_len = 0, signature_len = 0; + const EVP_MD *sign_md = NULL; + unsigned char *der = NULL; + int der_len; + int res; + EVP_MD_CTX *md_ctx = NULL; + ECDSA_SIG *sig = NULL; + BIGNUM *r = NULL, *s = NULL; + const struct dpp_curve_params *curve; + EC_KEY *eckey; + const EC_GROUP *group; + int nid; + + eckey = EVP_PKEY_get1_EC_KEY(csign_pub); + if (!eckey) + goto fail; + group = EC_KEY_get0_group(eckey); + if (!group) + goto fail; + nid = EC_GROUP_get_curve_name(group); + curve = dpp_get_curve_nid(nid); + if (!curve) + goto fail; + wpa_printf(MSG_DEBUG, "DPP: C-sign-key group: %s", curve->jwk_crv); + os_memset(info, 0, sizeof(*info)); + + signed_start = pos = connector; + end = os_strchr(pos, '.'); + if (!end) { + wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector"); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + prot_hdr = base64_url_decode((const unsigned char *) pos, + end - pos, &prot_hdr_len); + if (!prot_hdr) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to base64url decode signedConnector JWS Protected Header"); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + wpa_hexdump_ascii(MSG_DEBUG, + "DPP: signedConnector - JWS Protected Header", + prot_hdr, prot_hdr_len); + kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md); + if (!kid) { + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + if (wpabuf_len(kid) != SHA256_MAC_LEN) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)", + (unsigned int) wpabuf_len(kid), SHA256_MAC_LEN); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + + pos = end + 1; + end = os_strchr(pos, '.'); + if (!end) { + wpa_printf(MSG_DEBUG, + "DPP: Missing dot(2) in signedConnector"); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + signed_end = end - 1; + info->payload = base64_url_decode((const unsigned char *) pos, + end - pos, &info->payload_len); + if (!info->payload) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to base64url decode signedConnector JWS Payload"); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + wpa_hexdump_ascii(MSG_DEBUG, + "DPP: signedConnector - JWS Payload", + info->payload, info->payload_len); + pos = end + 1; + signature = base64_url_decode((const unsigned char *) pos, + os_strlen(pos), &signature_len); + if (!signature) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to base64url decode signedConnector signature"); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature", + signature, signature_len); + + if (dpp_check_pubkey_match(csign_pub, kid) < 0) { + ret = DPP_STATUS_NO_MATCH; + goto fail; + } + + if (signature_len & 0x01) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected signedConnector signature length (%d)", + (int) signature_len); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + + /* JWS Signature encodes the signature (r,s) as two octet strings. Need + * to convert that to DER encoded ECDSA_SIG for OpenSSL EVP routines. */ + r = BN_bin2bn(signature, signature_len / 2, NULL); + s = BN_bin2bn(signature + signature_len / 2, signature_len / 2, NULL); + sig = ECDSA_SIG_new(); + if (!r || !s || !sig || ECDSA_SIG_set0(sig, r, s) != 1) + goto fail; + r = NULL; + s = NULL; + + der_len = i2d_ECDSA_SIG(sig, &der); + if (der_len <= 0) { + wpa_printf(MSG_DEBUG, "DPP: Could not DER encode signature"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: DER encoded signature", der, der_len); + md_ctx = EVP_MD_CTX_create(); + if (!md_ctx) + goto fail; + + ERR_clear_error(); + if (EVP_DigestVerifyInit(md_ctx, NULL, sign_md, NULL, csign_pub) != 1) { + wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyInit failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + if (EVP_DigestVerifyUpdate(md_ctx, signed_start, + signed_end - signed_start + 1) != 1) { + wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyUpdate failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + res = EVP_DigestVerifyFinal(md_ctx, der, der_len); + if (res != 1) { + wpa_printf(MSG_DEBUG, + "DPP: EVP_DigestVerifyFinal failed (res=%d): %s", + res, ERR_error_string(ERR_get_error(), NULL)); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + + ret = DPP_STATUS_OK; +fail: + EC_KEY_free(eckey); + EVP_MD_CTX_destroy(md_ctx); + os_free(prot_hdr); + wpabuf_free(kid); + os_free(signature); + ECDSA_SIG_free(sig); + BN_free(r); + BN_free(s); + OPENSSL_free(der); + return ret; +} + + +static int dpp_parse_cred_dpp(struct dpp_authentication *auth, + struct json_token *cred) +{ + struct dpp_signed_connector_info info; + struct json_token *token, *csign; + int ret = -1; + EVP_PKEY *csign_pub = NULL; + const struct dpp_curve_params *key_curve = NULL; + const char *signed_connector; + + os_memset(&info, 0, sizeof(info)); + + wpa_printf(MSG_DEBUG, "DPP: Connector credential"); + + csign = json_get_member(cred, "csign"); + if (!csign || csign->type != JSON_OBJECT) { + wpa_printf(MSG_DEBUG, "DPP: No csign JWK in JSON"); + goto fail; + } + + csign_pub = dpp_parse_jwk(csign, &key_curve); + if (!csign_pub) { + wpa_printf(MSG_DEBUG, "DPP: Failed to parse csign JWK"); + goto fail; + } + dpp_debug_print_key("DPP: Received C-sign-key", csign_pub); + + token = json_get_member(cred, "signedConnector"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, "DPP: No signedConnector string found"); + goto fail; + } + wpa_hexdump_ascii(MSG_DEBUG, "DPP: signedConnector", + token->string, os_strlen(token->string)); + signed_connector = token->string; + + if (os_strchr(signed_connector, '"') || + os_strchr(signed_connector, '\n')) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected character in signedConnector"); + goto fail; + } + + if (dpp_process_signed_connector(&info, csign_pub, + signed_connector) != DPP_STATUS_OK) + goto fail; + + if (dpp_parse_connector(auth, info.payload, info.payload_len) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Failed to parse connector"); + goto fail; + } + + os_free(auth->connector); + auth->connector = os_strdup(signed_connector); + + dpp_copy_csign(auth, csign_pub); + dpp_copy_netaccesskey(auth); + + ret = 0; +fail: + EVP_PKEY_free(csign_pub); + os_free(info.payload); + return ret; +} + + +const char * dpp_akm_str(enum dpp_akm akm) +{ + switch (akm) { + case DPP_AKM_DPP: + return "dpp"; + case DPP_AKM_PSK: + return "psk"; + case DPP_AKM_SAE: + return "sae"; + case DPP_AKM_PSK_SAE: + return "psk+sae"; + default: + return "??"; + } +} + + +static enum dpp_akm dpp_akm_from_str(const char *akm) +{ + if (os_strcmp(akm, "psk") == 0) + return DPP_AKM_PSK; + if (os_strcmp(akm, "sae") == 0) + return DPP_AKM_SAE; + if (os_strcmp(akm, "psk+sae") == 0) + return DPP_AKM_PSK_SAE; + if (os_strcmp(akm, "dpp") == 0) + return DPP_AKM_DPP; + return DPP_AKM_UNKNOWN; +} + + +static int dpp_parse_conf_obj(struct dpp_authentication *auth, + const u8 *conf_obj, u16 conf_obj_len) +{ + int ret = -1; + struct json_token *root, *token, *discovery, *cred; + + root = json_parse((const char *) conf_obj, conf_obj_len); + if (!root) + return -1; + if (root->type != JSON_OBJECT) { + dpp_auth_fail(auth, "JSON root is not an object"); + goto fail; + } + + token = json_get_member(root, "wi-fi_tech"); + if (!token || token->type != JSON_STRING) { + dpp_auth_fail(auth, "No wi-fi_tech string value found"); + goto fail; + } + if (os_strcmp(token->string, "infra") != 0) { + wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech value: '%s'", + token->string); + dpp_auth_fail(auth, "Unsupported wi-fi_tech value"); + goto fail; + } + + discovery = json_get_member(root, "discovery"); + if (!discovery || discovery->type != JSON_OBJECT) { + dpp_auth_fail(auth, "No discovery object in JSON"); + goto fail; + } + + token = json_get_member(discovery, "ssid"); + if (!token || token->type != JSON_STRING) { + dpp_auth_fail(auth, "No discovery::ssid string value found"); + goto fail; + } + wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid", + token->string, os_strlen(token->string)); + if (os_strlen(token->string) > SSID_MAX_LEN) { + dpp_auth_fail(auth, "Too long discovery::ssid string value"); + goto fail; + } + auth->ssid_len = os_strlen(token->string); + os_memcpy(auth->ssid, token->string, auth->ssid_len); + + cred = json_get_member(root, "cred"); + if (!cred || cred->type != JSON_OBJECT) { + dpp_auth_fail(auth, "No cred object in JSON"); + goto fail; + } + + token = json_get_member(cred, "akm"); + if (!token || token->type != JSON_STRING) { + dpp_auth_fail(auth, "No cred::akm string value found"); + goto fail; + } + auth->akm = dpp_akm_from_str(token->string); + + if (auth->akm == DPP_AKM_PSK || auth->akm == DPP_AKM_SAE || + auth->akm == DPP_AKM_PSK_SAE) { + if (dpp_parse_cred_legacy(auth, cred) < 0) + goto fail; + } else if (auth->akm == DPP_AKM_DPP) { + if (dpp_parse_cred_dpp(auth, cred) < 0) + goto fail; + } else { + wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s", + token->string); + dpp_auth_fail(auth, "Unsupported akm"); + goto fail; + } + + wpa_printf(MSG_DEBUG, "DPP: JSON parsing completed successfully"); + ret = 0; +fail: + json_free(root); + return ret; +} + + +int dpp_conf_resp_rx(struct dpp_authentication *auth, + const struct wpabuf *resp) +{ + const u8 *wrapped_data, *e_nonce, *status, *conf_obj; + u16 wrapped_data_len, e_nonce_len, status_len, conf_obj_len; + const u8 *addr[1]; + size_t len[1]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + int ret = -1; + + if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) { + dpp_auth_fail(auth, "Invalid attribute in config response"); + return -1; + } + + wrapped_data = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp), + DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + dpp_auth_fail(auth, + "Missing or invalid required Wrapped Data attribute"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + return -1; + + addr[0] = wpabuf_head(resp); + len[0] = wrapped_data - 4 - (const u8 *) wpabuf_head(resp); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); + + if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 1, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + e_nonce = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_ENROLLEE_NONCE, + &e_nonce_len); + if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, + "Missing or invalid Enrollee Nonce attribute"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); + if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) { + dpp_auth_fail(auth, "Enrollee Nonce mismatch"); + goto fail; + } + + status = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp), + DPP_ATTR_STATUS, &status_len); + if (!status || status_len < 1) { + dpp_auth_fail(auth, + "Missing or invalid required DPP Status attribute"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); + if (status[0] != DPP_STATUS_OK) { + dpp_auth_fail(auth, "Configurator rejected configuration"); + goto fail; + } + + conf_obj = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_CONFIG_OBJ, &conf_obj_len); + if (!conf_obj) { + dpp_auth_fail(auth, + "Missing required Configuration Object attribute"); + goto fail; + } + wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON", + conf_obj, conf_obj_len); + if (dpp_parse_conf_obj(auth, conf_obj, conf_obj_len) < 0) + goto fail; + + ret = 0; + +fail: + os_free(unwrapped); + return ret; +} + + +void dpp_configurator_free(struct dpp_configurator *conf) +{ + if (!conf) + return; + EVP_PKEY_free(conf->csign); + os_free(conf->kid); + os_free(conf); +} + + +int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf, + size_t buflen) +{ + EC_KEY *eckey; + int key_len, ret = -1; + unsigned char *key = NULL; + + if (!conf->csign) + return -1; + + eckey = EVP_PKEY_get1_EC_KEY(conf->csign); + if (!eckey) + return -1; + + key_len = i2d_ECPrivateKey(eckey, &key); + if (key_len > 0) + ret = wpa_snprintf_hex(buf, buflen, key, key_len); + + EC_KEY_free(eckey); + OPENSSL_free(key); + return ret; +} + + +struct dpp_configurator * +dpp_keygen_configurator(const char *curve, const u8 *privkey, + size_t privkey_len) +{ + struct dpp_configurator *conf; + struct wpabuf *csign_pub = NULL; + u8 kid_hash[SHA256_MAC_LEN]; + const u8 *addr[1]; + size_t len[1]; + + conf = os_zalloc(sizeof(*conf)); + if (!conf) + return NULL; + + if (!curve) { + conf->curve = &dpp_curves[0]; + } else { + conf->curve = dpp_get_curve_name(curve); + if (!conf->curve) { + wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", + curve); + os_free(conf); + return NULL; + } + } + if (privkey) + conf->csign = dpp_set_keypair(&conf->curve, privkey, + privkey_len); + else + conf->csign = dpp_gen_keypair(conf->curve); + if (!conf->csign) + goto fail; + conf->own = 1; + + csign_pub = dpp_get_pubkey_point(conf->csign, 1); + if (!csign_pub) { + wpa_printf(MSG_INFO, "DPP: Failed to extract C-sign-key"); + goto fail; + } + + /* kid = SHA256(ANSI X9.63 uncompressed C-sign-key) */ + addr[0] = wpabuf_head(csign_pub); + len[0] = wpabuf_len(csign_pub); + if (sha256_vector(1, addr, len, kid_hash) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to derive kid for C-sign-key"); + goto fail; + } + + conf->kid = (char *) base64_url_encode(kid_hash, sizeof(kid_hash), + NULL, 0); + if (!conf->kid) + goto fail; +out: + wpabuf_free(csign_pub); + return conf; +fail: + dpp_configurator_free(conf); + conf = NULL; + goto out; +} + + +int dpp_configurator_own_config(struct dpp_authentication *auth, + const char *curve, int ap) +{ + struct wpabuf *conf_obj; + int ret = -1; + + if (!auth->conf) { + wpa_printf(MSG_DEBUG, "DPP: No configurator specified"); + return -1; + } + + if (!curve) { + auth->curve = &dpp_curves[0]; + } else { + auth->curve = dpp_get_curve_name(curve); + if (!auth->curve) { + wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", + curve); + return -1; + } + } + wpa_printf(MSG_DEBUG, + "DPP: Building own configuration/connector with curve %s", + auth->curve->name); + + auth->own_protocol_key = dpp_gen_keypair(auth->curve); + if (!auth->own_protocol_key) + return -1; + dpp_copy_netaccesskey(auth); + auth->peer_protocol_key = auth->own_protocol_key; + dpp_copy_csign(auth, auth->conf->csign); + + conf_obj = dpp_build_conf_obj(auth, ap); + if (!conf_obj) + goto fail; + ret = dpp_parse_conf_obj(auth, wpabuf_head(conf_obj), + wpabuf_len(conf_obj)); +fail: + wpabuf_free(conf_obj); + auth->peer_protocol_key = NULL; + return ret; +} + + +static int dpp_compatible_netrole(const char *role1, const char *role2) +{ + return (os_strcmp(role1, "sta") == 0 && os_strcmp(role2, "ap") == 0) || + (os_strcmp(role1, "ap") == 0 && os_strcmp(role2, "sta") == 0); +} + + +static int dpp_connector_compatible_group(struct json_token *root, + const char *group_id, + const char *net_role) +{ + struct json_token *groups, *token; + + groups = json_get_member(root, "groups"); + if (!groups || groups->type != JSON_ARRAY) + return 0; + + for (token = groups->child; token; token = token->sibling) { + struct json_token *id, *role; + + id = json_get_member(token, "groupId"); + if (!id || id->type != JSON_STRING) + continue; + + role = json_get_member(token, "netRole"); + if (!role || role->type != JSON_STRING) + continue; + + if (os_strcmp(id->string, "*") != 0 && + os_strcmp(group_id, "*") != 0 && + os_strcmp(id->string, group_id) != 0) + continue; + + if (dpp_compatible_netrole(role->string, net_role)) + return 1; + } + + return 0; +} + + +static int dpp_connector_match_groups(struct json_token *own_root, + struct json_token *peer_root) +{ + struct json_token *groups, *token; + + groups = json_get_member(peer_root, "groups"); + if (!groups || groups->type != JSON_ARRAY) { + wpa_printf(MSG_DEBUG, "DPP: No peer groups array found"); + return 0; + } + + for (token = groups->child; token; token = token->sibling) { + struct json_token *id, *role; + + id = json_get_member(token, "groupId"); + if (!id || id->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, + "DPP: Missing peer groupId string"); + continue; + } + + role = json_get_member(token, "netRole"); + if (!role || role->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, + "DPP: Missing peer groups::netRole string"); + continue; + } + wpa_printf(MSG_DEBUG, + "DPP: peer connector group: groupId='%s' netRole='%s'", + id->string, role->string); + if (dpp_connector_compatible_group(own_root, id->string, + role->string)) { + wpa_printf(MSG_DEBUG, + "DPP: Compatible group/netRole in own connector"); + return 1; + } + } + + return 0; +} + + +static int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk, + unsigned int hash_len) +{ + u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN]; + const char *info = "DPP PMK"; + int res; + + /* PMK = HKDF(<>, "DPP PMK", N.x) */ + + /* HKDF-Extract(<>, N.x) */ + os_memset(salt, 0, hash_len); + if (dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)", + prk, hash_len); + + /* HKDF-Expand(PRK, info, L) */ + res = dpp_hkdf_expand(hash_len, prk, hash_len, info, pmk, hash_len); + os_memset(prk, 0, hash_len); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "DPP: PMK = HKDF-Expand(PRK, info, L)", + pmk, hash_len); + return 0; +} + + +static int dpp_derive_pmkid(const struct dpp_curve_params *curve, + EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid) +{ + struct wpabuf *nkx, *pkx; + int ret = -1, res; + const u8 *addr[2]; + size_t len[2]; + u8 hash[SHA256_MAC_LEN]; + + /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */ + nkx = dpp_get_pubkey_point(own_key, 0); + pkx = dpp_get_pubkey_point(peer_key, 0); + if (!nkx || !pkx) + goto fail; + addr[0] = wpabuf_head(nkx); + len[0] = wpabuf_len(nkx) / 2; + addr[1] = wpabuf_head(pkx); + len[1] = wpabuf_len(pkx) / 2; + if (len[0] != len[1]) + goto fail; + if (os_memcmp(addr[0], addr[1], len[0]) > 0) { + addr[0] = wpabuf_head(pkx); + addr[1] = wpabuf_head(nkx); + } + wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 1", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 2", addr[1], len[1]); + res = sha256_vector(2, addr, len, hash); + if (res < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash output", hash, SHA256_MAC_LEN); + os_memcpy(pmkid, hash, PMKID_LEN); + wpa_hexdump(MSG_DEBUG, "DPP: PMKID", pmkid, PMKID_LEN); + ret = 0; +fail: + wpabuf_free(nkx); + wpabuf_free(pkx); + return ret; +} + + +enum dpp_status_error +dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector, + const u8 *net_access_key, size_t net_access_key_len, + const u8 *csign_key, size_t csign_key_len, + const u8 *peer_connector, size_t peer_connector_len, + os_time_t *expiry) +{ + struct json_token *root = NULL, *netkey, *token; + struct json_token *own_root = NULL; + enum dpp_status_error ret = 255, res; + EVP_PKEY *own_key = NULL, *peer_key = NULL; + struct wpabuf *own_key_pub = NULL; + const struct dpp_curve_params *curve, *own_curve; + struct dpp_signed_connector_info info; + const unsigned char *p; + EVP_PKEY *csign = NULL; + char *signed_connector = NULL; + const char *pos, *end; + unsigned char *own_conn = NULL; + size_t own_conn_len; + EVP_PKEY_CTX *ctx = NULL; + size_t Nx_len; + u8 Nx[DPP_MAX_SHARED_SECRET_LEN]; + + os_memset(intro, 0, sizeof(*intro)); + os_memset(&info, 0, sizeof(info)); + if (expiry) + *expiry = 0; + + p = csign_key; + csign = d2i_PUBKEY(NULL, &p, csign_key_len); + if (!csign) { + wpa_printf(MSG_ERROR, + "DPP: Failed to parse local C-sign-key information"); + goto fail; + } + + own_key = dpp_set_keypair(&own_curve, net_access_key, + net_access_key_len); + if (!own_key) { + wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey"); + goto fail; + } + + pos = os_strchr(own_connector, '.'); + if (!pos) { + wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the first dot (.)"); + goto fail; + } + pos++; + end = os_strchr(pos, '.'); + if (!end) { + wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)"); + goto fail; + } + own_conn = base64_url_decode((const unsigned char *) pos, + end - pos, &own_conn_len); + if (!own_conn) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to base64url decode own signedConnector JWS Payload"); + goto fail; + } + + own_root = json_parse((const char *) own_conn, own_conn_len); + if (!own_root) { + wpa_printf(MSG_DEBUG, "DPP: Failed to parse local connector"); + goto fail; + } + + wpa_hexdump_ascii(MSG_DEBUG, "DPP: Peer signedConnector", + peer_connector, peer_connector_len); + signed_connector = os_malloc(peer_connector_len + 1); + if (!signed_connector) + goto fail; + os_memcpy(signed_connector, peer_connector, peer_connector_len); + signed_connector[peer_connector_len] = '\0'; + + res = dpp_process_signed_connector(&info, csign, signed_connector); + if (res != DPP_STATUS_OK) { + ret = res; + goto fail; + } + + root = json_parse((const char *) info.payload, info.payload_len); + if (!root) { + wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed"); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + + if (!dpp_connector_match_groups(own_root, root)) { + wpa_printf(MSG_DEBUG, + "DPP: Peer connector does not include compatible group netrole with own connector"); + ret = DPP_STATUS_NO_MATCH; + goto fail; + } + + token = json_get_member(root, "expiry"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, + "DPP: No expiry string found - connector does not expire"); + } else { + wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string); + if (dpp_key_expired(token->string, expiry)) { + wpa_printf(MSG_DEBUG, + "DPP: Connector (netAccessKey) has expired"); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + } + + netkey = json_get_member(root, "netAccessKey"); + if (!netkey || netkey->type != JSON_OBJECT) { + wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found"); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + + peer_key = dpp_parse_jwk(netkey, &curve); + if (!peer_key) { + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + dpp_debug_print_key("DPP: Received netAccessKey", peer_key); + + if (own_curve != curve) { + wpa_printf(MSG_DEBUG, + "DPP: Mismatching netAccessKey curves (%s != %s)", + own_curve->name, curve->name); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + + /* ECDH: N = nk * PK */ + ctx = EVP_PKEY_CTX_new(own_key, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, peer_key) != 1 || + EVP_PKEY_derive(ctx, NULL, &Nx_len) != 1 || + Nx_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, Nx, &Nx_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", + Nx, Nx_len); + + /* PMK = HKDF(<>, "DPP PMK", N.x) */ + if (dpp_derive_pmk(Nx, Nx_len, intro->pmk, curve->hash_len) < 0) { + wpa_printf(MSG_ERROR, "DPP: Failed to derive PMK"); + goto fail; + } + intro->pmk_len = curve->hash_len; + + /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */ + if (dpp_derive_pmkid(curve, own_key, peer_key, intro->pmkid) < 0) { + wpa_printf(MSG_ERROR, "DPP: Failed to derive PMKID"); + goto fail; + } + + ret = DPP_STATUS_OK; +fail: + if (ret != DPP_STATUS_OK) + os_memset(intro, 0, sizeof(*intro)); + os_memset(Nx, 0, sizeof(Nx)); + EVP_PKEY_CTX_free(ctx); + os_free(own_conn); + os_free(signed_connector); + os_free(info.payload); + EVP_PKEY_free(own_key); + wpabuf_free(own_key_pub); + EVP_PKEY_free(peer_key); + EVP_PKEY_free(csign); + json_free(root); + json_free(own_root); + return ret; +} + + +static EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve, + int init) +{ + EC_GROUP *group; + size_t len = curve->prime_len; + const u8 *x, *y; + + switch (curve->ike_group) { + case 19: + x = init ? pkex_init_x_p256 : pkex_resp_x_p256; + y = init ? pkex_init_y_p256 : pkex_resp_y_p256; + break; + case 20: + x = init ? pkex_init_x_p384 : pkex_resp_x_p384; + y = init ? pkex_init_y_p384 : pkex_resp_y_p384; + break; + case 21: + x = init ? pkex_init_x_p521 : pkex_resp_x_p521; + y = init ? pkex_init_y_p521 : pkex_resp_y_p521; + break; + case 28: + x = init ? pkex_init_x_bp_p256r1 : pkex_resp_x_bp_p256r1; + y = init ? pkex_init_y_bp_p256r1 : pkex_resp_y_bp_p256r1; + break; + case 29: + x = init ? pkex_init_x_bp_p384r1 : pkex_resp_x_bp_p384r1; + y = init ? pkex_init_y_bp_p384r1 : pkex_resp_y_bp_p384r1; + break; + case 30: + x = init ? pkex_init_x_bp_p512r1 : pkex_resp_x_bp_p512r1; + y = init ? pkex_init_y_bp_p512r1 : pkex_resp_y_bp_p512r1; + break; + default: + return NULL; + } + + group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name)); + if (!group) + return NULL; + return dpp_set_pubkey_point_group(group, x, y, len); +} + + +static EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve, + const u8 *mac_init, const char *code, + const char *identifier, BN_CTX *bnctx, + const EC_GROUP **ret_group) +{ + u8 hash[DPP_MAX_HASH_LEN]; + const u8 *addr[3]; + size_t len[3]; + unsigned int num_elem = 0; + EC_POINT *Qi = NULL; + EVP_PKEY *Pi = NULL; + EC_KEY *Pi_ec = NULL; + const EC_POINT *Pi_point; + BIGNUM *hash_bn = NULL; + const EC_GROUP *group = NULL; + EC_GROUP *group2 = NULL; + + /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */ + + wpa_printf(MSG_DEBUG, "DPP: MAC-Initiator: " MACSTR, MAC2STR(mac_init)); + addr[num_elem] = mac_init; + len[num_elem] = ETH_ALEN; + num_elem++; + if (identifier) { + wpa_printf(MSG_DEBUG, "DPP: code identifier: %s", + identifier); + addr[num_elem] = (const u8 *) identifier; + len[num_elem] = os_strlen(identifier); + num_elem++; + } + wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code)); + addr[num_elem] = (const u8 *) code; + len[num_elem] = os_strlen(code); + num_elem++; + if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0) + goto fail; + wpa_hexdump_key(MSG_DEBUG, + "DPP: H(MAC-Initiator | [identifier |] code)", + hash, curve->hash_len); + Pi = dpp_pkex_get_role_elem(curve, 1); + if (!Pi) + goto fail; + dpp_debug_print_key("DPP: Pi", Pi); + Pi_ec = EVP_PKEY_get1_EC_KEY(Pi); + if (!Pi_ec) + goto fail; + Pi_point = EC_KEY_get0_public_key(Pi_ec); + + group = EC_KEY_get0_group(Pi_ec); + if (!group) + goto fail; + group2 = EC_GROUP_dup(group); + if (!group2) + goto fail; + Qi = EC_POINT_new(group2); + if (!Qi) { + EC_GROUP_free(group2); + goto fail; + } + hash_bn = BN_bin2bn(hash, curve->hash_len, NULL); + if (!hash_bn || + EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1) + goto fail; + if (EC_POINT_is_at_infinity(group, Qi)) { + wpa_printf(MSG_INFO, "DPP: Qi is the point-at-infinity"); + goto fail; + } + dpp_debug_print_point("DPP: Qi", group, Qi); +out: + EC_KEY_free(Pi_ec); + EVP_PKEY_free(Pi); + BN_clear_free(hash_bn); + if (ret_group) + *ret_group = group2; + return Qi; +fail: + EC_POINT_free(Qi); + Qi = NULL; + goto out; +} + + +static EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, + const u8 *mac_resp, const char *code, + const char *identifier, BN_CTX *bnctx, + const EC_GROUP **ret_group) +{ + u8 hash[DPP_MAX_HASH_LEN]; + const u8 *addr[3]; + size_t len[3]; + unsigned int num_elem = 0; + EC_POINT *Qr = NULL; + EVP_PKEY *Pr = NULL; + EC_KEY *Pr_ec = NULL; + const EC_POINT *Pr_point; + BIGNUM *hash_bn = NULL; + const EC_GROUP *group = NULL; + EC_GROUP *group2 = NULL; + + /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */ + + wpa_printf(MSG_DEBUG, "DPP: MAC-Responder: " MACSTR, MAC2STR(mac_resp)); + addr[num_elem] = mac_resp; + len[num_elem] = ETH_ALEN; + num_elem++; + if (identifier) { + wpa_printf(MSG_DEBUG, "DPP: code identifier: %s", + identifier); + addr[num_elem] = (const u8 *) identifier; + len[num_elem] = os_strlen(identifier); + num_elem++; + } + wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code)); + addr[num_elem] = (const u8 *) code; + len[num_elem] = os_strlen(code); + num_elem++; + if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0) + goto fail; + wpa_hexdump_key(MSG_DEBUG, + "DPP: H(MAC-Responder | [identifier |] code)", + hash, curve->hash_len); + Pr = dpp_pkex_get_role_elem(curve, 0); + if (!Pr) + goto fail; + dpp_debug_print_key("DPP: Pr", Pr); + Pr_ec = EVP_PKEY_get1_EC_KEY(Pr); + if (!Pr_ec) + goto fail; + Pr_point = EC_KEY_get0_public_key(Pr_ec); + + group = EC_KEY_get0_group(Pr_ec); + if (!group) + goto fail; + group2 = EC_GROUP_dup(group); + if (!group2) + goto fail; + Qr = EC_POINT_new(group2); + if (!Qr) { + EC_GROUP_free(group2); + goto fail; + } + hash_bn = BN_bin2bn(hash, curve->hash_len, NULL); + if (!hash_bn || + EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1) + goto fail; + if (EC_POINT_is_at_infinity(group, Qr)) { + wpa_printf(MSG_INFO, "DPP: Qr is the point-at-infinity"); + goto fail; + } + dpp_debug_print_point("DPP: Qr", group, Qr); +out: + EC_KEY_free(Pr_ec); + EVP_PKEY_free(Pr); + BN_clear_free(hash_bn); + if (ret_group) + *ret_group = group2; + return Qr; +fail: + EC_POINT_free(Qr); + Qr = NULL; + goto out; +} + + +#ifdef CONFIG_TESTING_OPTIONS +static int dpp_test_gen_invalid_key(struct wpabuf *msg, + const struct dpp_curve_params *curve) +{ + BN_CTX *ctx; + BIGNUM *x, *y; + int ret = -1; + EC_GROUP *group; + EC_POINT *point; + + group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name)); + if (!group) + return -1; + + ctx = BN_CTX_new(); + point = EC_POINT_new(group); + x = BN_new(); + y = BN_new(); + if (!ctx || !point || !x || !y) + goto fail; + + if (BN_rand(x, curve->prime_len * 8, 0, 0) != 1) + goto fail; + + /* Generate a random y coordinate that results in a point that is not + * on the curve. */ + for (;;) { + if (BN_rand(y, curve->prime_len * 8, 0, 0) != 1) + goto fail; + + if (EC_POINT_set_affine_coordinates_GFp(group, point, x, y, + ctx) != 1) { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(OPENSSL_IS_BORINGSSL) + /* Unlike older OpenSSL versions, OpenSSL 1.1.1 and BoringSSL + * return an error from EC_POINT_set_affine_coordinates_GFp() + * when the point is not on the curve. */ + break; +#else /* >=1.1.0 or OPENSSL_IS_BORINGSSL */ + goto fail; +#endif /* >= 1.1.0 or OPENSSL_IS_BORINGSSL */ + } + + if (!EC_POINT_is_on_curve(group, point, ctx)) + break; + } + + if (dpp_bn2bin_pad(x, wpabuf_put(msg, curve->prime_len), + curve->prime_len) < 0 || + dpp_bn2bin_pad(y, wpabuf_put(msg, curve->prime_len), + curve->prime_len) < 0) + goto fail; + + ret = 0; +fail: + if (ret < 0) + wpa_printf(MSG_INFO, "DPP: Failed to generate invalid key"); + BN_free(x); + BN_free(y); + EC_POINT_free(point); + BN_CTX_free(ctx); + + return ret; +} +#endif /* CONFIG_TESTING_OPTIONS */ + + +static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex) +{ + EC_KEY *X_ec = NULL; + const EC_POINT *X_point; + BN_CTX *bnctx = NULL; + const EC_GROUP *group; + EC_POINT *Qi = NULL, *M = NULL; + struct wpabuf *M_buf = NULL; + BIGNUM *Mx = NULL, *My = NULL; + struct wpabuf *msg = NULL; + size_t attr_len; + const struct dpp_curve_params *curve = pkex->own_bi->curve; + + wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request"); + + /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */ + bnctx = BN_CTX_new(); + if (!bnctx) + goto fail; + Qi = dpp_pkex_derive_Qi(curve, pkex->own_mac, pkex->code, + pkex->identifier, bnctx, &group); + if (!Qi) + goto fail; + + /* Generate a random ephemeral keypair x/X */ +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_pkex_ephemeral_key_override_len) { + const struct dpp_curve_params *tmp_curve; + + wpa_printf(MSG_INFO, + "DPP: TESTING - override ephemeral key x/X"); + pkex->x = dpp_set_keypair(&tmp_curve, + dpp_pkex_ephemeral_key_override, + dpp_pkex_ephemeral_key_override_len); + } else { + pkex->x = dpp_gen_keypair(curve); + } +#else /* CONFIG_TESTING_OPTIONS */ + pkex->x = dpp_gen_keypair(curve); +#endif /* CONFIG_TESTING_OPTIONS */ + if (!pkex->x) + goto fail; + + /* M = X + Qi */ + X_ec = EVP_PKEY_get1_EC_KEY(pkex->x); + if (!X_ec) + goto fail; + X_point = EC_KEY_get0_public_key(X_ec); + if (!X_point) + goto fail; + dpp_debug_print_point("DPP: X", group, X_point); + M = EC_POINT_new(group); + Mx = BN_new(); + My = BN_new(); + if (!M || !Mx || !My || + EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 || + EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1) + goto fail; + dpp_debug_print_point("DPP: M", group, M); + + /* Initiator -> Responder: group, [identifier,] M */ + attr_len = 4 + 2; + if (pkex->identifier) + attr_len += 4 + os_strlen(pkex->identifier); + attr_len += 4 + 2 * curve->prime_len; + msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_REQ, attr_len); + if (!msg) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Finite Cyclic Group"); + goto skip_finite_cyclic_group; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Finite Cyclic Group attribute */ + wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP); + wpabuf_put_le16(msg, 2); + wpabuf_put_le16(msg, curve->ike_group); + +#ifdef CONFIG_TESTING_OPTIONS +skip_finite_cyclic_group: +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Code Identifier attribute */ + if (pkex->identifier) { + wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER); + wpabuf_put_le16(msg, os_strlen(pkex->identifier)); + wpabuf_put_str(msg, pkex->identifier); + } + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key"); + goto out; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* M in Encrypted Key attribute */ + wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY); + wpabuf_put_le16(msg, 2 * curve->prime_len); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key"); + if (dpp_test_gen_invalid_key(msg, curve) < 0) + goto fail; + goto out; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (dpp_bn2bin_pad(Mx, wpabuf_put(msg, curve->prime_len), + curve->prime_len) < 0 || + dpp_bn2bin_pad(Mx, pkex->Mx, curve->prime_len) < 0 || + dpp_bn2bin_pad(My, wpabuf_put(msg, curve->prime_len), + curve->prime_len) < 0) + goto fail; + +out: + wpabuf_free(M_buf); + EC_KEY_free(X_ec); + EC_POINT_free(M); + EC_POINT_free(Qi); + BN_clear_free(Mx); + BN_clear_free(My); + BN_CTX_free(bnctx); + return msg; +fail: + wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request"); + wpabuf_free(msg); + msg = NULL; + goto out; +} + + +static void dpp_pkex_fail(struct dpp_pkex *pkex, const char *txt) +{ + wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt); +} + + +struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi, + const u8 *own_mac, + const char *identifier, + const char *code) +{ + struct dpp_pkex *pkex; + +#ifdef CONFIG_TESTING_OPTIONS + if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) { + wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR, + MAC2STR(dpp_pkex_own_mac_override)); + own_mac = dpp_pkex_own_mac_override; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + pkex = os_zalloc(sizeof(*pkex)); + if (!pkex) + return NULL; + pkex->msg_ctx = msg_ctx; + pkex->initiator = 1; + pkex->own_bi = bi; + os_memcpy(pkex->own_mac, own_mac, ETH_ALEN); + if (identifier) { + pkex->identifier = os_strdup(identifier); + if (!pkex->identifier) + goto fail; + } + pkex->code = os_strdup(code); + if (!pkex->code) + goto fail; + pkex->exchange_req = dpp_pkex_build_exchange_req(pkex); + if (!pkex->exchange_req) + goto fail; + return pkex; +fail: + dpp_pkex_free(pkex); + return NULL; +} + + +static struct wpabuf * +dpp_pkex_build_exchange_resp(struct dpp_pkex *pkex, + enum dpp_status_error status, + const BIGNUM *Nx, const BIGNUM *Ny) +{ + struct wpabuf *msg = NULL; + size_t attr_len; + const struct dpp_curve_params *curve = pkex->own_bi->curve; + + /* Initiator -> Responder: DPP Status, [identifier,] N */ + attr_len = 4 + 1; + if (pkex->identifier) + attr_len += 4 + os_strlen(pkex->identifier); + attr_len += 4 + 2 * curve->prime_len; + msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len); + if (!msg) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); + goto skip_status; + } + + if (dpp_test == DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); + status = 255; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* DPP Status */ + dpp_build_attr_status(msg, status); + +#ifdef CONFIG_TESTING_OPTIONS +skip_status: +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Code Identifier attribute */ + if (pkex->identifier) { + wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER); + wpabuf_put_le16(msg, os_strlen(pkex->identifier)); + wpabuf_put_str(msg, pkex->identifier); + } + + if (status != DPP_STATUS_OK) + goto skip_encrypted_key; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key"); + goto skip_encrypted_key; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* N in Encrypted Key attribute */ + wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY); + wpabuf_put_le16(msg, 2 * curve->prime_len); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key"); + if (dpp_test_gen_invalid_key(msg, curve) < 0) + goto fail; + goto skip_encrypted_key; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (dpp_bn2bin_pad(Nx, wpabuf_put(msg, curve->prime_len), + curve->prime_len) < 0 || + dpp_bn2bin_pad(Nx, pkex->Nx, curve->prime_len) < 0 || + dpp_bn2bin_pad(Ny, wpabuf_put(msg, curve->prime_len), + curve->prime_len) < 0) + goto fail; + +skip_encrypted_key: + if (status == DPP_STATUS_BAD_GROUP) { + /* Finite Cyclic Group attribute */ + wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP); + wpabuf_put_le16(msg, 2); + wpabuf_put_le16(msg, curve->ike_group); + } + + return msg; +fail: + wpabuf_free(msg); + return NULL; +} + + +static int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp, + const u8 *Mx, size_t Mx_len, + const u8 *Nx, size_t Nx_len, + const char *code, + const u8 *Kx, size_t Kx_len, + u8 *z, unsigned int hash_len) +{ + u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN]; + int res; + u8 *info, *pos; + size_t info_len; + + /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x) + */ + + /* HKDF-Extract(<>, IKM=K.x) */ + os_memset(salt, 0, hash_len); + if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)", + prk, hash_len); + info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code); + info = os_malloc(info_len); + if (!info) + return -1; + pos = info; + os_memcpy(pos, mac_init, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, mac_resp, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, Mx, Mx_len); + pos += Mx_len; + os_memcpy(pos, Nx, Nx_len); + pos += Nx_len; + os_memcpy(pos, code, os_strlen(code)); + + /* HKDF-Expand(PRK, info, L) */ + if (hash_len == 32) + res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len, + z, hash_len); + else if (hash_len == 48) + res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len, + z, hash_len); + else if (hash_len == 64) + res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len, + z, hash_len); + else + res = -1; + os_free(info); + os_memset(prk, 0, hash_len); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)", + z, hash_len); + return 0; +} + + +static int dpp_pkex_identifier_match(const u8 *attr_id, u16 attr_id_len, + const char *identifier) +{ + if (!attr_id && identifier) { + wpa_printf(MSG_DEBUG, + "DPP: No PKEX code identifier received, but expected one"); + return 0; + } + + if (attr_id && !identifier) { + wpa_printf(MSG_DEBUG, + "DPP: PKEX code identifier received, but not expecting one"); + return 0; + } + + if (attr_id && identifier && + (os_strlen(identifier) != attr_id_len || + os_memcmp(identifier, attr_id, attr_id_len) != 0)) { + wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch"); + return 0; + } + + return 1; +} + + +struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx, + struct dpp_bootstrap_info *bi, + const u8 *own_mac, + const u8 *peer_mac, + const char *identifier, + const char *code, + const u8 *buf, size_t len) +{ + const u8 *attr_group, *attr_id, *attr_key; + u16 attr_group_len, attr_id_len, attr_key_len; + const struct dpp_curve_params *curve = bi->curve; + u16 ike_group; + struct dpp_pkex *pkex = NULL; + EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL; + BN_CTX *bnctx = NULL; + const EC_GROUP *group; + BIGNUM *Mx = NULL, *My = NULL; + EC_KEY *Y_ec = NULL, *X_ec = NULL;; + const EC_POINT *Y_point; + BIGNUM *Nx = NULL, *Ny = NULL; + u8 Kx[DPP_MAX_SHARED_SECRET_LEN]; + size_t Kx_len; + int res; + EVP_PKEY_CTX *ctx = NULL; + + if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) { + wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "PKEX counter t limit reached - ignore message"); + return NULL; + } + +#ifdef CONFIG_TESTING_OPTIONS + if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) { + wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR, + MAC2STR(dpp_pkex_peer_mac_override)); + peer_mac = dpp_pkex_peer_mac_override; + } + if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) { + wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR, + MAC2STR(dpp_pkex_own_mac_override)); + own_mac = dpp_pkex_own_mac_override; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + attr_id_len = 0; + attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER, + &attr_id_len); + if (!dpp_pkex_identifier_match(attr_id, attr_id_len, identifier)) + return NULL; + + attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP, + &attr_group_len); + if (!attr_group || attr_group_len != 2) { + wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Missing or invalid Finite Cyclic Group attribute"); + return NULL; + } + ike_group = WPA_GET_LE16(attr_group); + if (ike_group != curve->ike_group) { + wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Mismatching PKEX curve: peer=%u own=%u", + ike_group, curve->ike_group); + pkex = os_zalloc(sizeof(*pkex)); + if (!pkex) + goto fail; + pkex->own_bi = bi; + pkex->failed = 1; + pkex->exchange_resp = dpp_pkex_build_exchange_resp( + pkex, DPP_STATUS_BAD_GROUP, NULL, NULL); + if (!pkex->exchange_resp) + goto fail; + return pkex; + } + + /* M in Encrypted Key attribute */ + attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY, + &attr_key_len); + if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 || + attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) { + wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Missing Encrypted Key attribute"); + return NULL; + } + + /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */ + bnctx = BN_CTX_new(); + if (!bnctx) + goto fail; + Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx, + &group); + if (!Qi) + goto fail; + + /* X' = M - Qi */ + X = EC_POINT_new(group); + M = EC_POINT_new(group); + Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL); + My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL); + if (!X || !M || !Mx || !My || + EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 || + EC_POINT_is_at_infinity(group, M) || + !EC_POINT_is_on_curve(group, M, bnctx) || + EC_POINT_invert(group, Qi, bnctx) != 1 || + EC_POINT_add(group, X, M, Qi, bnctx) != 1 || + EC_POINT_is_at_infinity(group, X) || + !EC_POINT_is_on_curve(group, X, bnctx)) { + wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Invalid Encrypted Key value"); + bi->pkex_t++; + goto fail; + } + dpp_debug_print_point("DPP: M", group, M); + dpp_debug_print_point("DPP: X'", group, X); + + pkex = os_zalloc(sizeof(*pkex)); + if (!pkex) + goto fail; + pkex->t = bi->pkex_t; + pkex->msg_ctx = msg_ctx; + pkex->own_bi = bi; + os_memcpy(pkex->own_mac, own_mac, ETH_ALEN); + os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN); + if (identifier) { + pkex->identifier = os_strdup(identifier); + if (!pkex->identifier) + goto fail; + } + pkex->code = os_strdup(code); + if (!pkex->code) + goto fail; + + os_memcpy(pkex->Mx, attr_key, attr_key_len / 2); + + X_ec = EC_KEY_new(); + if (!X_ec || + EC_KEY_set_group(X_ec, group) != 1 || + EC_KEY_set_public_key(X_ec, X) != 1) + goto fail; + pkex->x = EVP_PKEY_new(); + if (!pkex->x || + EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1) + goto fail; + + /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */ + Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL); + if (!Qr) + goto fail; + + /* Generate a random ephemeral keypair y/Y */ +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_pkex_ephemeral_key_override_len) { + const struct dpp_curve_params *tmp_curve; + + wpa_printf(MSG_INFO, + "DPP: TESTING - override ephemeral key y/Y"); + pkex->y = dpp_set_keypair(&tmp_curve, + dpp_pkex_ephemeral_key_override, + dpp_pkex_ephemeral_key_override_len); + } else { + pkex->y = dpp_gen_keypair(curve); + } +#else /* CONFIG_TESTING_OPTIONS */ + pkex->y = dpp_gen_keypair(curve); +#endif /* CONFIG_TESTING_OPTIONS */ + if (!pkex->y) + goto fail; + + /* N = Y + Qr */ + Y_ec = EVP_PKEY_get1_EC_KEY(pkex->y); + if (!Y_ec) + goto fail; + Y_point = EC_KEY_get0_public_key(Y_ec); + if (!Y_point) + goto fail; + dpp_debug_print_point("DPP: Y", group, Y_point); + N = EC_POINT_new(group); + Nx = BN_new(); + Ny = BN_new(); + if (!N || !Nx || !Ny || + EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 || + EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1) + goto fail; + dpp_debug_print_point("DPP: N", group, N); + + pkex->exchange_resp = dpp_pkex_build_exchange_resp(pkex, DPP_STATUS_OK, + Nx, Ny); + if (!pkex->exchange_resp) + goto fail; + + /* K = y * X' */ + ctx = EVP_PKEY_CTX_new(pkex->y, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 || + EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 || + Kx_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)", + Kx, Kx_len); + + /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x) + */ + res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac, + pkex->Mx, curve->prime_len, + pkex->Nx, curve->prime_len, pkex->code, + Kx, Kx_len, pkex->z, curve->hash_len); + os_memset(Kx, 0, Kx_len); + if (res < 0) + goto fail; + + pkex->exchange_done = 1; + +out: + EVP_PKEY_CTX_free(ctx); + BN_CTX_free(bnctx); + EC_POINT_free(Qi); + EC_POINT_free(Qr); + BN_free(Mx); + BN_free(My); + BN_free(Nx); + BN_free(Ny); + EC_POINT_free(M); + EC_POINT_free(N); + EC_POINT_free(X); + EC_KEY_free(X_ec); + EC_KEY_free(Y_ec); + return pkex; +fail: + wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed"); + dpp_pkex_free(pkex); + pkex = NULL; + goto out; +} + + +static struct wpabuf * +dpp_pkex_build_commit_reveal_req(struct dpp_pkex *pkex, + const struct wpabuf *A_pub, const u8 *u) +{ + const struct dpp_curve_params *curve = pkex->own_bi->curve; + struct wpabuf *msg = NULL; + size_t clear_len, attr_len; + struct wpabuf *clear = NULL; + u8 *wrapped; + u8 octet; + const u8 *addr[2]; + size_t len[2]; + + /* {A, u, [bootstrapping info]}z */ + clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len; + clear = wpabuf_alloc(clear_len); + attr_len = 4 + clear_len + AES_BLOCK_SIZE; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) + attr_len += 5; +#endif /* CONFIG_TESTING_OPTIONS */ + msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len); + if (!clear || !msg) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key"); + goto skip_bootstrap_key; + } + if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key"); + wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY); + wpabuf_put_le16(clear, 2 * curve->prime_len); + if (dpp_test_gen_invalid_key(clear, curve) < 0) + goto fail; + goto skip_bootstrap_key; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* A in Bootstrap Key attribute */ + wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY); + wpabuf_put_le16(clear, wpabuf_len(A_pub)); + wpabuf_put_buf(clear, A_pub); + +#ifdef CONFIG_TESTING_OPTIONS +skip_bootstrap_key: + if (dpp_test == DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-Auth tag"); + goto skip_i_auth_tag; + } + if (dpp_test == DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - I-Auth tag mismatch"); + wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG); + wpabuf_put_le16(clear, curve->hash_len); + wpabuf_put_data(clear, u, curve->hash_len - 1); + wpabuf_put_u8(clear, u[curve->hash_len - 1] ^ 0x01); + goto skip_i_auth_tag; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* u in I-Auth tag attribute */ + wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG); + wpabuf_put_le16(clear, curve->hash_len); + wpabuf_put_data(clear, u, curve->hash_len); + +#ifdef CONFIG_TESTING_OPTIONS +skip_i_auth_tag: + if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); + goto skip_wrapped_data; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + addr[0] = wpabuf_head_u8(msg) + 2; + len[0] = DPP_HDR_LEN; + octet = 0; + addr[1] = &octet; + len[1] = sizeof(octet); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + + wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); + if (aes_siv_encrypt(pkex->z, curve->hash_len, + wpabuf_head(clear), wpabuf_len(clear), + 2, addr, len, wrapped) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); + dpp_build_attr_status(msg, DPP_STATUS_OK); + } +skip_wrapped_data: +#endif /* CONFIG_TESTING_OPTIONS */ + +out: + wpabuf_free(clear); + return msg; + +fail: + wpabuf_free(msg); + msg = NULL; + goto out; +} + + +struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex, + const u8 *peer_mac, + const u8 *buf, size_t buflen) +{ + const u8 *attr_status, *attr_id, *attr_key, *attr_group; + u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len; + const EC_GROUP *group; + BN_CTX *bnctx = NULL; + struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL; + const struct dpp_curve_params *curve = pkex->own_bi->curve; + EC_POINT *Qr = NULL, *Y = NULL, *N = NULL; + BIGNUM *Nx = NULL, *Ny = NULL; + EVP_PKEY_CTX *ctx = NULL; + EC_KEY *Y_ec = NULL; + size_t Jx_len, Kx_len; + u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN]; + const u8 *addr[4]; + size_t len[4]; + u8 u[DPP_MAX_HASH_LEN]; + int res; + + if (pkex->failed || pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator) + return NULL; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at PKEX Exchange Response"); + pkex->failed = 1; + return NULL; + } + + if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) { + wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR, + MAC2STR(dpp_pkex_peer_mac_override)); + peer_mac = dpp_pkex_peer_mac_override; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN); + + attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS, + &attr_status_len); + if (!attr_status || attr_status_len != 1) { + dpp_pkex_fail(pkex, "No DPP Status attribute"); + return NULL; + } + wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]); + + if (attr_status[0] == DPP_STATUS_BAD_GROUP) { + attr_group = dpp_get_attr(buf, buflen, + DPP_ATTR_FINITE_CYCLIC_GROUP, + &attr_group_len); + if (attr_group && attr_group_len == 2) { + wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Peer indicated mismatching PKEX group - proposed %u", + WPA_GET_LE16(attr_group)); + return NULL; + } + } + + if (attr_status[0] != DPP_STATUS_OK) { + dpp_pkex_fail(pkex, "PKEX failed (peer indicated failure)"); + return NULL; + } + + attr_id_len = 0; + attr_id = dpp_get_attr(buf, buflen, DPP_ATTR_CODE_IDENTIFIER, + &attr_id_len); + if (!dpp_pkex_identifier_match(attr_id, attr_id_len, + pkex->identifier)) { + dpp_pkex_fail(pkex, "PKEX code identifier mismatch"); + return NULL; + } + + /* N in Encrypted Key attribute */ + attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY, + &attr_key_len); + if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) { + dpp_pkex_fail(pkex, "Missing Encrypted Key attribute"); + return NULL; + } + + /* Qr = H(MAC-Responder | [identifier |] code) * Pr */ + bnctx = BN_CTX_new(); + if (!bnctx) + goto fail; + Qr = dpp_pkex_derive_Qr(curve, pkex->peer_mac, pkex->code, + pkex->identifier, bnctx, &group); + if (!Qr) + goto fail; + + /* Y' = N - Qr */ + Y = EC_POINT_new(group); + N = EC_POINT_new(group); + Nx = BN_bin2bn(attr_key, attr_key_len / 2, NULL); + Ny = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL); + if (!Y || !N || !Nx || !Ny || + EC_POINT_set_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1 || + EC_POINT_is_at_infinity(group, N) || + !EC_POINT_is_on_curve(group, N, bnctx) || + EC_POINT_invert(group, Qr, bnctx) != 1 || + EC_POINT_add(group, Y, N, Qr, bnctx) != 1 || + EC_POINT_is_at_infinity(group, Y) || + !EC_POINT_is_on_curve(group, Y, bnctx)) { + dpp_pkex_fail(pkex, "Invalid Encrypted Key value"); + pkex->t++; + goto fail; + } + dpp_debug_print_point("DPP: N", group, N); + dpp_debug_print_point("DPP: Y'", group, Y); + + pkex->exchange_done = 1; + + /* ECDH: J = a * Y’ */ + Y_ec = EC_KEY_new(); + if (!Y_ec || + EC_KEY_set_group(Y_ec, group) != 1 || + EC_KEY_set_public_key(Y_ec, Y) != 1) + goto fail; + pkex->y = EVP_PKEY_new(); + if (!pkex->y || + EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1) + goto fail; + ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 || + EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 || + Jx_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)", + Jx, Jx_len); + + /* u = HMAC(J.x, MAC-Initiator | A.x | Y’.x | X.x ) */ + A_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0); + Y_pub = dpp_get_pubkey_point(pkex->y, 0); + X_pub = dpp_get_pubkey_point(pkex->x, 0); + if (!A_pub || !Y_pub || !X_pub) + goto fail; + addr[0] = pkex->own_mac; + len[0] = ETH_ALEN; + addr[1] = wpabuf_head(A_pub); + len[1] = wpabuf_len(A_pub) / 2; + addr[2] = wpabuf_head(Y_pub); + len[2] = wpabuf_len(Y_pub) / 2; + addr[3] = wpabuf_head(X_pub); + len[3] = wpabuf_len(X_pub) / 2; + if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len); + + /* K = x * Y’ */ + EVP_PKEY_CTX_free(ctx); + ctx = EVP_PKEY_CTX_new(pkex->x, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 || + EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 || + Kx_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)", + Kx, Kx_len); + + /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x) + */ + res = dpp_pkex_derive_z(pkex->own_mac, pkex->peer_mac, + pkex->Mx, curve->prime_len, + attr_key /* N.x */, attr_key_len / 2, + pkex->code, Kx, Kx_len, + pkex->z, curve->hash_len); + os_memset(Kx, 0, Kx_len); + if (res < 0) + goto fail; + + msg = dpp_pkex_build_commit_reveal_req(pkex, A_pub, u); + if (!msg) + goto fail; + +out: + wpabuf_free(A_pub); + wpabuf_free(X_pub); + wpabuf_free(Y_pub); + EC_POINT_free(Qr); + EC_POINT_free(Y); + EC_POINT_free(N); + BN_free(Nx); + BN_free(Ny); + EC_KEY_free(Y_ec); + EVP_PKEY_CTX_free(ctx); + BN_CTX_free(bnctx); + return msg; +fail: + wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed"); + goto out; +} + + +static struct wpabuf * +dpp_pkex_build_commit_reveal_resp(struct dpp_pkex *pkex, + const struct wpabuf *B_pub, const u8 *v) +{ + const struct dpp_curve_params *curve = pkex->own_bi->curve; + struct wpabuf *msg = NULL; + const u8 *addr[2]; + size_t len[2]; + u8 octet; + u8 *wrapped; + struct wpabuf *clear = NULL; + size_t clear_len, attr_len; + + /* {B, v [bootstrapping info]}z */ + clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len; + clear = wpabuf_alloc(clear_len); + attr_len = 4 + clear_len + AES_BLOCK_SIZE; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP) + attr_len += 5; +#endif /* CONFIG_TESTING_OPTIONS */ + msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len); + if (!clear || !msg) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key"); + goto skip_bootstrap_key; + } + if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key"); + wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY); + wpabuf_put_le16(clear, 2 * curve->prime_len); + if (dpp_test_gen_invalid_key(clear, curve) < 0) + goto fail; + goto skip_bootstrap_key; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* B in Bootstrap Key attribute */ + wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY); + wpabuf_put_le16(clear, wpabuf_len(B_pub)); + wpabuf_put_buf(clear, B_pub); + +#ifdef CONFIG_TESTING_OPTIONS +skip_bootstrap_key: + if (dpp_test == DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth tag"); + goto skip_r_auth_tag; + } + if (dpp_test == DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - R-Auth tag mismatch"); + wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG); + wpabuf_put_le16(clear, curve->hash_len); + wpabuf_put_data(clear, v, curve->hash_len - 1); + wpabuf_put_u8(clear, v[curve->hash_len - 1] ^ 0x01); + goto skip_r_auth_tag; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* v in R-Auth tag attribute */ + wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG); + wpabuf_put_le16(clear, curve->hash_len); + wpabuf_put_data(clear, v, curve->hash_len); + +#ifdef CONFIG_TESTING_OPTIONS +skip_r_auth_tag: + if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); + goto skip_wrapped_data; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + addr[0] = wpabuf_head_u8(msg) + 2; + len[0] = DPP_HDR_LEN; + octet = 1; + addr[1] = &octet; + len[1] = sizeof(octet); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + + wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); + if (aes_siv_encrypt(pkex->z, curve->hash_len, + wpabuf_head(clear), wpabuf_len(clear), + 2, addr, len, wrapped) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); + dpp_build_attr_status(msg, DPP_STATUS_OK); + } +skip_wrapped_data: +#endif /* CONFIG_TESTING_OPTIONS */ + +out: + wpabuf_free(clear); + return msg; + +fail: + wpabuf_free(msg); + msg = NULL; + goto out; +} + + +struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex, + const u8 *hdr, + const u8 *buf, size_t buflen) +{ + const struct dpp_curve_params *curve = pkex->own_bi->curve; + EVP_PKEY_CTX *ctx = NULL; + size_t Jx_len, Lx_len; + u8 Jx[DPP_MAX_SHARED_SECRET_LEN]; + u8 Lx[DPP_MAX_SHARED_SECRET_LEN]; + const u8 *wrapped_data, *b_key, *peer_u; + u16 wrapped_data_len, b_key_len, peer_u_len = 0; + const u8 *addr[4]; + size_t len[4]; + u8 octet; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL; + struct wpabuf *B_pub = NULL; + u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN]; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_REQ) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at PKEX CR Request"); + pkex->failed = 1; + return NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (!pkex->exchange_done || pkex->failed || + pkex->t >= PKEX_COUNTER_T_LIMIT || pkex->initiator) + goto fail; + + wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + dpp_pkex_fail(pkex, + "Missing or invalid required Wrapped Data attribute"); + goto fail; + } + + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + octet = 0; + addr[1] = &octet; + len[1] = sizeof(octet); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + if (aes_siv_decrypt(pkex->z, curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_pkex_fail(pkex, + "AES-SIV decryption failed - possible PKEX code mismatch"); + pkex->failed = 1; + pkex->t++; + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data"); + goto fail; + } + + b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY, + &b_key_len); + if (!b_key || b_key_len != 2 * curve->prime_len) { + dpp_pkex_fail(pkex, "No valid peer bootstrapping key found"); + goto fail; + } + pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key, + b_key_len); + if (!pkex->peer_bootstrap_key) { + dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid"); + goto fail; + } + dpp_debug_print_key("DPP: Peer bootstrap public key", + pkex->peer_bootstrap_key); + + /* ECDH: J' = y * A' */ + ctx = EVP_PKEY_CTX_new(pkex->y, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 || + EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 || + Jx_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)", + Jx, Jx_len); + + /* u' = HMAC(J'.x, MAC-Initiator | A'.x | Y.x | X'.x) */ + A_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0); + Y_pub = dpp_get_pubkey_point(pkex->y, 0); + X_pub = dpp_get_pubkey_point(pkex->x, 0); + if (!A_pub || !Y_pub || !X_pub) + goto fail; + addr[0] = pkex->peer_mac; + len[0] = ETH_ALEN; + addr[1] = wpabuf_head(A_pub); + len[1] = wpabuf_len(A_pub) / 2; + addr[2] = wpabuf_head(Y_pub); + len[2] = wpabuf_len(Y_pub) / 2; + addr[3] = wpabuf_head(X_pub); + len[3] = wpabuf_len(X_pub) / 2; + if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0) + goto fail; + + peer_u = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG, + &peer_u_len); + if (!peer_u || peer_u_len != curve->hash_len || + os_memcmp(peer_u, u, curve->hash_len) != 0) { + dpp_pkex_fail(pkex, "No valid u (I-Auth tag) found"); + wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'", + u, curve->hash_len); + wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len); + pkex->t++; + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received"); + + /* ECDH: L = b * X' */ + EVP_PKEY_CTX_free(ctx); + ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 || + EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 || + Lx_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)", + Lx, Lx_len); + + /* v = HMAC(L.x, MAC-Responder | B.x | X'.x | Y.x) */ + B_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0); + if (!B_pub) + goto fail; + addr[0] = pkex->own_mac; + len[0] = ETH_ALEN; + addr[1] = wpabuf_head(B_pub); + len[1] = wpabuf_len(B_pub) / 2; + addr[2] = wpabuf_head(X_pub); + len[2] = wpabuf_len(X_pub) / 2; + addr[3] = wpabuf_head(Y_pub); + len[3] = wpabuf_len(Y_pub) / 2; + if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len); + + msg = dpp_pkex_build_commit_reveal_resp(pkex, B_pub, v); + if (!msg) + goto fail; + +out: + EVP_PKEY_CTX_free(ctx); + os_free(unwrapped); + wpabuf_free(A_pub); + wpabuf_free(B_pub); + wpabuf_free(X_pub); + wpabuf_free(Y_pub); + return msg; +fail: + wpa_printf(MSG_DEBUG, + "DPP: PKEX Commit-Reveal Request processing failed"); + goto out; +} + + +int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr, + const u8 *buf, size_t buflen) +{ + const struct dpp_curve_params *curve = pkex->own_bi->curve; + const u8 *wrapped_data, *b_key, *peer_v; + u16 wrapped_data_len, b_key_len, peer_v_len = 0; + const u8 *addr[4]; + size_t len[4]; + u8 octet; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + int ret = -1; + u8 v[DPP_MAX_HASH_LEN]; + size_t Lx_len; + u8 Lx[DPP_MAX_SHARED_SECRET_LEN]; + EVP_PKEY_CTX *ctx = NULL; + struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at PKEX CR Response"); + pkex->failed = 1; + goto fail; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (!pkex->exchange_done || pkex->failed || + pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator) + goto fail; + + wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + dpp_pkex_fail(pkex, + "Missing or invalid required Wrapped Data attribute"); + goto fail; + } + + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + octet = 1; + addr[1] = &octet; + len[1] = sizeof(octet); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + if (aes_siv_decrypt(pkex->z, curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_pkex_fail(pkex, + "AES-SIV decryption failed - possible PKEX code mismatch"); + pkex->t++; + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data"); + goto fail; + } + + b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY, + &b_key_len); + if (!b_key || b_key_len != 2 * curve->prime_len) { + dpp_pkex_fail(pkex, "No valid peer bootstrapping key found"); + goto fail; + } + pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key, + b_key_len); + if (!pkex->peer_bootstrap_key) { + dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid"); + goto fail; + } + dpp_debug_print_key("DPP: Peer bootstrap public key", + pkex->peer_bootstrap_key); + + /* ECDH: L' = x * B' */ + ctx = EVP_PKEY_CTX_new(pkex->x, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 || + EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 || + Lx_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)", + Lx, Lx_len); + + /* v' = HMAC(L.x, MAC-Responder | B'.x | X.x | Y'.x) */ + B_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0); + X_pub = dpp_get_pubkey_point(pkex->x, 0); + Y_pub = dpp_get_pubkey_point(pkex->y, 0); + if (!B_pub || !X_pub || !Y_pub) + goto fail; + addr[0] = pkex->peer_mac; + len[0] = ETH_ALEN; + addr[1] = wpabuf_head(B_pub); + len[1] = wpabuf_len(B_pub) / 2; + addr[2] = wpabuf_head(X_pub); + len[2] = wpabuf_len(X_pub) / 2; + addr[3] = wpabuf_head(Y_pub); + len[3] = wpabuf_len(Y_pub) / 2; + if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0) + goto fail; + + peer_v = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_AUTH_TAG, + &peer_v_len); + if (!peer_v || peer_v_len != curve->hash_len || + os_memcmp(peer_v, v, curve->hash_len) != 0) { + dpp_pkex_fail(pkex, "No valid v (R-Auth tag) found"); + wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'", + v, curve->hash_len); + wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len); + pkex->t++; + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received"); + + ret = 0; +out: + wpabuf_free(B_pub); + wpabuf_free(X_pub); + wpabuf_free(Y_pub); + EVP_PKEY_CTX_free(ctx); + os_free(unwrapped); + return ret; +fail: + goto out; +} + + +void dpp_pkex_free(struct dpp_pkex *pkex) +{ + if (!pkex) + return; + + os_free(pkex->identifier); + os_free(pkex->code); + EVP_PKEY_free(pkex->x); + EVP_PKEY_free(pkex->y); + EVP_PKEY_free(pkex->peer_bootstrap_key); + wpabuf_free(pkex->exchange_req); + wpabuf_free(pkex->exchange_resp); + os_free(pkex); +} + + +#ifdef CONFIG_TESTING_OPTIONS +char * dpp_corrupt_connector_signature(const char *connector) +{ + char *tmp, *pos, *signed3 = NULL; + unsigned char *signature = NULL; + size_t signature_len = 0, signed3_len; + + tmp = os_zalloc(os_strlen(connector) + 5); + if (!tmp) + goto fail; + os_memcpy(tmp, connector, os_strlen(connector)); + + pos = os_strchr(tmp, '.'); + if (!pos) + goto fail; + + pos = os_strchr(pos + 1, '.'); + if (!pos) + goto fail; + pos++; + + wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s", + pos); + signature = base64_url_decode((const unsigned char *) pos, + os_strlen(pos), &signature_len); + if (!signature || signature_len == 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature", + signature, signature_len); + signature[signature_len - 1] ^= 0x01; + wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature", + signature, signature_len); + signed3 = (char *) base64_url_encode(signature, signature_len, + &signed3_len, 0); + if (!signed3) + goto fail; + os_memcpy(pos, signed3, signed3_len); + pos[signed3_len] = '\0'; + wpa_printf(MSG_DEBUG, "DPP: Corrupted base64url encoded signature: %s", + pos); + +out: + os_free(signature); + os_free(signed3); + return tmp; +fail: + os_free(tmp); + tmp = NULL; + goto out; +} +#endif /* CONFIG_TESTING_OPTIONS */ diff --git a/src/common/dpp.h b/src/common/dpp.h new file mode 100644 index 000000000000..25759088a57e --- /dev/null +++ b/src/common/dpp.h @@ -0,0 +1,435 @@ +/* + * DPP functionality shared between hostapd and wpa_supplicant + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DPP_H +#define DPP_H + +#include <openssl/x509.h> + +#include "utils/list.h" +#include "common/wpa_common.h" +#include "crypto/sha256.h" + +#define DPP_HDR_LEN (4 + 2) /* OUI, OUI Type, Crypto Suite, DPP frame type */ + +enum dpp_public_action_frame_type { + DPP_PA_AUTHENTICATION_REQ = 0, + DPP_PA_AUTHENTICATION_RESP = 1, + DPP_PA_AUTHENTICATION_CONF = 2, + DPP_PA_PEER_DISCOVERY_REQ = 5, + DPP_PA_PEER_DISCOVERY_RESP = 6, + DPP_PA_PKEX_EXCHANGE_REQ = 7, + DPP_PA_PKEX_EXCHANGE_RESP = 8, + DPP_PA_PKEX_COMMIT_REVEAL_REQ = 9, + DPP_PA_PKEX_COMMIT_REVEAL_RESP = 10, +}; + +enum dpp_attribute_id { + DPP_ATTR_STATUS = 0x1000, + DPP_ATTR_I_BOOTSTRAP_KEY_HASH = 0x1001, + DPP_ATTR_R_BOOTSTRAP_KEY_HASH = 0x1002, + DPP_ATTR_I_PROTOCOL_KEY = 0x1003, + DPP_ATTR_WRAPPED_DATA = 0x1004, + DPP_ATTR_I_NONCE = 0x1005, + DPP_ATTR_I_CAPABILITIES = 0x1006, + DPP_ATTR_R_NONCE = 0x1007, + DPP_ATTR_R_CAPABILITIES = 0x1008, + DPP_ATTR_R_PROTOCOL_KEY = 0x1009, + DPP_ATTR_I_AUTH_TAG = 0x100A, + DPP_ATTR_R_AUTH_TAG = 0x100B, + DPP_ATTR_CONFIG_OBJ = 0x100C, + DPP_ATTR_CONNECTOR = 0x100D, + DPP_ATTR_CONFIG_ATTR_OBJ = 0x100E, + DPP_ATTR_BOOTSTRAP_KEY = 0x100F, + DPP_ATTR_OWN_NET_NK_HASH = 0x1011, + DPP_ATTR_FINITE_CYCLIC_GROUP = 0x1012, + DPP_ATTR_ENCRYPTED_KEY = 0x1013, + DPP_ATTR_ENROLLEE_NONCE = 0x1014, + DPP_ATTR_CODE_IDENTIFIER = 0x1015, + DPP_ATTR_TRANSACTION_ID = 0x1016, + DPP_ATTR_BOOTSTRAP_INFO = 0x1017, + DPP_ATTR_CHANNEL = 0x1018, +}; + +enum dpp_status_error { + DPP_STATUS_OK = 0, + DPP_STATUS_NOT_COMPATIBLE = 1, + DPP_STATUS_AUTH_FAILURE = 2, + DPP_STATUS_UNWRAP_FAILURE = 3, + DPP_STATUS_BAD_GROUP = 4, + DPP_STATUS_CONFIGURE_FAILURE = 5, + DPP_STATUS_RESPONSE_PENDING = 6, + DPP_STATUS_INVALID_CONNECTOR = 7, + DPP_STATUS_NO_MATCH = 8, +}; + +#define DPP_CAPAB_ENROLLEE BIT(0) +#define DPP_CAPAB_CONFIGURATOR BIT(1) +#define DPP_CAPAB_ROLE_MASK (BIT(0) | BIT(1)) + +#define DPP_BOOTSTRAP_MAX_FREQ 30 +#define DPP_MAX_NONCE_LEN 32 +#define DPP_MAX_HASH_LEN 64 +#define DPP_MAX_SHARED_SECRET_LEN 66 + +struct dpp_curve_params { + const char *name; + size_t hash_len; + size_t aes_siv_key_len; + size_t nonce_len; + size_t prime_len; + const char *jwk_crv; + u16 ike_group; + const char *jws_alg; +}; + +enum dpp_bootstrap_type { + DPP_BOOTSTRAP_QR_CODE, + DPP_BOOTSTRAP_PKEX, +}; + +struct dpp_bootstrap_info { + struct dl_list list; + unsigned int id; + enum dpp_bootstrap_type type; + char *uri; + u8 mac_addr[ETH_ALEN]; + char *info; + unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ]; + unsigned int num_freq; + int own; + EVP_PKEY *pubkey; + u8 pubkey_hash[SHA256_MAC_LEN]; + const struct dpp_curve_params *curve; + unsigned int pkex_t; /* number of failures before dpp_pkex + * instantiation */ +}; + +#define PKEX_COUNTER_T_LIMIT 5 + +struct dpp_pkex { + void *msg_ctx; + unsigned int initiator:1; + unsigned int exchange_done:1; + unsigned int failed:1; + struct dpp_bootstrap_info *own_bi; + u8 own_mac[ETH_ALEN]; + u8 peer_mac[ETH_ALEN]; + char *identifier; + char *code; + EVP_PKEY *x; + EVP_PKEY *y; + u8 Mx[DPP_MAX_SHARED_SECRET_LEN]; + u8 Nx[DPP_MAX_SHARED_SECRET_LEN]; + u8 z[DPP_MAX_HASH_LEN]; + EVP_PKEY *peer_bootstrap_key; + struct wpabuf *exchange_req; + struct wpabuf *exchange_resp; + unsigned int t; /* number of failures on code use */ + unsigned int exch_req_wait_time; + unsigned int exch_req_tries; + unsigned int freq; +}; + +enum dpp_akm { + DPP_AKM_UNKNOWN, + DPP_AKM_DPP, + DPP_AKM_PSK, + DPP_AKM_SAE, + DPP_AKM_PSK_SAE +}; + +struct dpp_configuration { + u8 ssid[32]; + size_t ssid_len; + enum dpp_akm akm; + + /* For DPP configuration (connector) */ + os_time_t netaccesskey_expiry; + + /* TODO: groups */ + char *group_id; + + /* For legacy configuration */ + char *passphrase; + u8 psk[32]; +}; + +struct dpp_authentication { + void *msg_ctx; + const struct dpp_curve_params *curve; + struct dpp_bootstrap_info *peer_bi; + struct dpp_bootstrap_info *own_bi; + struct dpp_bootstrap_info *tmp_own_bi; + u8 waiting_pubkey_hash[SHA256_MAC_LEN]; + int response_pending; + enum dpp_status_error auth_resp_status; + u8 peer_mac_addr[ETH_ALEN]; + u8 i_nonce[DPP_MAX_NONCE_LEN]; + u8 r_nonce[DPP_MAX_NONCE_LEN]; + u8 e_nonce[DPP_MAX_NONCE_LEN]; + u8 i_capab; + u8 r_capab; + EVP_PKEY *own_protocol_key; + EVP_PKEY *peer_protocol_key; + struct wpabuf *req_msg; + struct wpabuf *resp_msg; + /* Intersection of possible frequencies for initiating DPP + * Authentication exchange */ + unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ]; + unsigned int num_freq, freq_idx; + unsigned int curr_freq; + unsigned int neg_freq; + unsigned int num_freq_iters; + size_t secret_len; + u8 Mx[DPP_MAX_SHARED_SECRET_LEN]; + size_t Mx_len; + u8 Nx[DPP_MAX_SHARED_SECRET_LEN]; + size_t Nx_len; + u8 Lx[DPP_MAX_SHARED_SECRET_LEN]; + size_t Lx_len; + u8 k1[DPP_MAX_HASH_LEN]; + u8 k2[DPP_MAX_HASH_LEN]; + u8 ke[DPP_MAX_HASH_LEN]; + int initiator; + int waiting_auth_resp; + int waiting_auth_conf; + int auth_req_ack; + unsigned int auth_resp_tries; + u8 allowed_roles; + int configurator; + int remove_on_tx_status; + int auth_success; + struct wpabuf *conf_req; + const struct wpabuf *conf_resp; /* owned by GAS server */ + struct dpp_configuration *conf_ap; + struct dpp_configuration *conf_sta; + struct dpp_configurator *conf; + char *connector; /* received signedConnector */ + u8 ssid[SSID_MAX_LEN]; + u8 ssid_len; + char passphrase[64]; + u8 psk[PMK_LEN]; + int psk_set; + enum dpp_akm akm; + struct wpabuf *net_access_key; + os_time_t net_access_key_expiry; + struct wpabuf *c_sign_key; +#ifdef CONFIG_TESTING_OPTIONS + char *config_obj_override; + char *discovery_override; + char *groups_override; + unsigned int ignore_netaccesskey_mismatch:1; +#endif /* CONFIG_TESTING_OPTIONS */ +}; + +struct dpp_configurator { + struct dl_list list; + unsigned int id; + int own; + EVP_PKEY *csign; + char *kid; + const struct dpp_curve_params *curve; +}; + +struct dpp_introduction { + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN_MAX]; + size_t pmk_len; +}; + +#ifdef CONFIG_TESTING_OPTIONS +enum dpp_test_behavior { + DPP_TEST_DISABLED = 0, + DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ = 1, + DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP = 2, + DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF = 3, + DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ = 4, + DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP = 5, + DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ = 6, + DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP = 7, + DPP_TEST_ZERO_I_CAPAB = 8, + DPP_TEST_ZERO_R_CAPAB = 9, + DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ = 10, + DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ = 11, + DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ = 12, + DPP_TEST_NO_I_NONCE_AUTH_REQ = 13, + DPP_TEST_NO_I_CAPAB_AUTH_REQ = 14, + DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ = 15, + DPP_TEST_NO_STATUS_AUTH_RESP = 16, + DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP = 17, + DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP = 18, + DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP = 19, + DPP_TEST_NO_R_NONCE_AUTH_RESP = 20, + DPP_TEST_NO_I_NONCE_AUTH_RESP = 21, + DPP_TEST_NO_R_CAPAB_AUTH_RESP = 22, + DPP_TEST_NO_R_AUTH_AUTH_RESP = 23, + DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP = 24, + DPP_TEST_NO_STATUS_AUTH_CONF = 25, + DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF = 26, + DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF = 27, + DPP_TEST_NO_I_AUTH_AUTH_CONF = 28, + DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF = 29, + DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP = 30, + DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP = 31, + DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP = 32, + DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF = 33, + DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ = 34, + DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ = 35, + DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP = 36, + DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP = 37, + DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ = 38, + DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ = 39, + DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ = 40, + DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP = 41, + DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP = 42, + DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP = 43, + DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ = 44, + DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP = 45, + DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP = 46, + DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ = 47, + DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP = 48, + DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ = 49, + DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP = 50, + DPP_TEST_NO_E_NONCE_CONF_REQ = 51, + DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ = 52, + DPP_TEST_NO_WRAPPED_DATA_CONF_REQ = 53, + DPP_TEST_NO_E_NONCE_CONF_RESP = 54, + DPP_TEST_NO_CONFIG_OBJ_CONF_RESP = 55, + DPP_TEST_NO_STATUS_CONF_RESP = 56, + DPP_TEST_NO_WRAPPED_DATA_CONF_RESP = 57, + DPP_TEST_INVALID_STATUS_CONF_RESP = 58, + DPP_TEST_E_NONCE_MISMATCH_CONF_RESP = 59, + DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_REQ = 60, + DPP_TEST_NO_CONNECTOR_PEER_DISC_REQ = 61, + DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_RESP = 62, + DPP_TEST_NO_STATUS_PEER_DISC_RESP = 63, + DPP_TEST_NO_CONNECTOR_PEER_DISC_RESP = 64, + DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF = 65, + DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ = 66, + DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP = 67, + DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ = 68, + DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ = 69, + DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP = 70, + DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP = 71, + DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF = 72, + DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF = 73, + DPP_TEST_INVALID_STATUS_AUTH_RESP = 74, + DPP_TEST_INVALID_STATUS_AUTH_CONF = 75, + DPP_TEST_INVALID_CONFIG_ATTR_OBJ_CONF_REQ = 76, + DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_RESP = 77, + DPP_TEST_INVALID_STATUS_PEER_DISC_RESP = 78, + DPP_TEST_INVALID_CONNECTOR_PEER_DISC_RESP = 79, + DPP_TEST_INVALID_CONNECTOR_PEER_DISC_REQ = 80, + DPP_TEST_INVALID_I_NONCE_AUTH_REQ = 81, + DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_REQ = 82, + DPP_TEST_INVALID_E_NONCE_CONF_REQ = 83, + DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP = 84, + DPP_TEST_STOP_AT_PKEX_CR_REQ = 85, + DPP_TEST_STOP_AT_PKEX_CR_RESP = 86, + DPP_TEST_STOP_AT_AUTH_REQ = 87, + DPP_TEST_STOP_AT_AUTH_RESP = 88, + DPP_TEST_STOP_AT_AUTH_CONF = 89, + DPP_TEST_STOP_AT_CONF_REQ = 90, +}; + +extern enum dpp_test_behavior dpp_test; +extern u8 dpp_pkex_own_mac_override[ETH_ALEN]; +extern u8 dpp_pkex_peer_mac_override[ETH_ALEN]; +extern u8 dpp_pkex_ephemeral_key_override[600]; +extern size_t dpp_pkex_ephemeral_key_override_len; +extern u8 dpp_protocol_key_override[600]; +extern size_t dpp_protocol_key_override_len; +extern u8 dpp_nonce_override[DPP_MAX_NONCE_LEN]; +extern size_t dpp_nonce_override_len; +#endif /* CONFIG_TESTING_OPTIONS */ + +void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info); +const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type); +int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi); +int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi, + const char *chan_list); +int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac); +int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info); +struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri); +char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve, + const u8 *privkey, size_t privkey_len); +struct hostapd_hw_modes; +struct dpp_authentication * dpp_auth_init(void *msg_ctx, + struct dpp_bootstrap_info *peer_bi, + struct dpp_bootstrap_info *own_bi, + u8 dpp_allowed_roles, + unsigned int neg_freq, + struct hostapd_hw_modes *own_modes, + u16 num_modes); +struct dpp_authentication * +dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual, + struct dpp_bootstrap_info *peer_bi, + struct dpp_bootstrap_info *own_bi, + unsigned int freq, const u8 *hdr, const u8 *attr_start, + size_t attr_len); +struct wpabuf * +dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr, + const u8 *attr_start, size_t attr_len); +struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth, + const char *json); +int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr, + const u8 *attr_start, size_t attr_len); +int dpp_notify_new_qr_code(struct dpp_authentication *auth, + struct dpp_bootstrap_info *peer_bi); +void dpp_configuration_free(struct dpp_configuration *conf); +void dpp_auth_deinit(struct dpp_authentication *auth); +struct wpabuf * +dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, + size_t attr_len); +int dpp_conf_resp_rx(struct dpp_authentication *auth, + const struct wpabuf *resp); +struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type, + size_t len); +const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len); +int dpp_check_attrs(const u8 *buf, size_t len); +int dpp_key_expired(const char *timestamp, os_time_t *expiry); +const char * dpp_akm_str(enum dpp_akm akm); +int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf, + size_t buflen); +void dpp_configurator_free(struct dpp_configurator *conf); +struct dpp_configurator * +dpp_keygen_configurator(const char *curve, const u8 *privkey, + size_t privkey_len); +int dpp_configurator_own_config(struct dpp_authentication *auth, + const char *curve, int ap); +enum dpp_status_error +dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector, + const u8 *net_access_key, size_t net_access_key_len, + const u8 *csign_key, size_t csign_key_len, + const u8 *peer_connector, size_t peer_connector_len, + os_time_t *expiry); +struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi, + const u8 *own_mac, + const char *identifier, + const char *code); +struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx, + struct dpp_bootstrap_info *bi, + const u8 *own_mac, + const u8 *peer_mac, + const char *identifier, + const char *code, + const u8 *buf, size_t len); +struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex, + const u8 *peer_mac, + const u8 *buf, size_t len); +struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex, + const u8 *hdr, + const u8 *buf, size_t len); +int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr, + const u8 *buf, size_t len); +void dpp_pkex_free(struct dpp_pkex *pkex); + +char * dpp_corrupt_connector_signature(const char *connector); + +#endif /* DPP_H */ diff --git a/src/common/gas.c b/src/common/gas.c index cff9254b7440..ba21b225efc9 100644 --- a/src/common/gas.c +++ b/src/common/gas.c @@ -75,7 +75,7 @@ gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, } -static struct wpabuf * +struct wpabuf * gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more, u16 comeback_delay, size_t size) { diff --git a/src/common/gas.h b/src/common/gas.h index 306adc58c6ee..4c93e3114ccd 100644 --- a/src/common/gas.h +++ b/src/common/gas.h @@ -14,6 +14,9 @@ struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size); struct wpabuf * gas_build_comeback_req(u8 dialog_token); struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, size_t size); +struct wpabuf * +gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more, + u16 comeback_delay, size_t size); struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size); struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, size_t size); diff --git a/src/common/gas_server.c b/src/common/gas_server.c new file mode 100644 index 000000000000..ca46758cec7c --- /dev/null +++ b/src/common/gas_server.c @@ -0,0 +1,487 @@ +/* + * Generic advertisement service (GAS) server + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "utils/common.h" +#include "utils/list.h" +#include "utils/eloop.h" +#include "ieee802_11_defs.h" +#include "gas.h" +#include "gas_server.h" + + +#define MAX_ADV_PROTO_ID_LEN 10 +#define GAS_QUERY_TIMEOUT 10 + +struct gas_server_handler { + struct dl_list list; + u8 adv_proto_id[MAX_ADV_PROTO_ID_LEN]; + u8 adv_proto_id_len; + struct wpabuf * (*req_cb)(void *ctx, const u8 *sa, + const u8 *query, size_t query_len); + void (*status_cb)(void *ctx, struct wpabuf *resp, int ok); + void *ctx; + struct gas_server *gas; +}; + +struct gas_server_response { + struct dl_list list; + size_t offset; + u8 frag_id; + struct wpabuf *resp; + int freq; + u8 dst[ETH_ALEN]; + u8 dialog_token; + struct gas_server_handler *handler; +}; + +struct gas_server { + struct dl_list handlers; /* struct gas_server_handler::list */ + struct dl_list responses; /* struct gas_server_response::list */ + void (*tx)(void *ctx, int freq, const u8 *da, struct wpabuf *resp, + unsigned int wait_time); + void *ctx; +}; + +static void gas_server_free_response(struct gas_server_response *response); + + +static void gas_server_response_timeout(void *eloop_ctx, void *user_ctx) +{ + struct gas_server_response *response = eloop_ctx; + + wpa_printf(MSG_DEBUG, "GAS: Response @%p timeout for " MACSTR + " (dialog_token=%u freq=%d frag_id=%u sent=%lu/%lu) - drop pending data", + response, MAC2STR(response->dst), response->dialog_token, + response->freq, response->frag_id, + (unsigned long) response->offset, + (unsigned long) wpabuf_len(response->resp)); + response->handler->status_cb(response->handler->ctx, + response->resp, 0); + response->resp = NULL; + dl_list_del(&response->list); + gas_server_free_response(response); +} + + +static void gas_server_free_response(struct gas_server_response *response) +{ + if (!response) + return; + wpa_printf(MSG_DEBUG, "DPP: Free GAS response @%p", response); + eloop_cancel_timeout(gas_server_response_timeout, response, NULL); + wpabuf_free(response->resp); + os_free(response); +} + + +static void +gas_server_send_resp(struct gas_server *gas, struct gas_server_handler *handler, + const u8 *da, int freq, u8 dialog_token, + struct wpabuf *query_resp) +{ + size_t max_len = (freq > 56160) ? 928 : 1400; + size_t hdr_len = 24 + 2 + 5 + 3 + handler->adv_proto_id_len + 2; + size_t resp_frag_len; + struct wpabuf *resp; + u16 comeback_delay; + struct gas_server_response *response; + + if (!query_resp) + return; + + response = os_zalloc(sizeof(*response)); + if (!response) { + wpabuf_free(query_resp); + return; + } + wpa_printf(MSG_DEBUG, "DPP: Allocated GAS response @%p", response); + response->freq = freq; + response->handler = handler; + os_memcpy(response->dst, da, ETH_ALEN); + response->dialog_token = dialog_token; + if (hdr_len + wpabuf_len(query_resp) > max_len) { + /* Need to use comeback to initiate fragmentation */ + comeback_delay = 1; + resp_frag_len = 0; + } else { + /* Full response fits into the initial response */ + comeback_delay = 0; + resp_frag_len = wpabuf_len(query_resp); + } + + resp = gas_build_initial_resp(dialog_token, WLAN_STATUS_SUCCESS, + comeback_delay, + handler->adv_proto_id_len + + resp_frag_len); + if (!resp) { + wpabuf_free(query_resp); + gas_server_free_response(response); + return; + } + + /* Advertisement Protocol element */ + wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */ + wpabuf_put_u8(resp, 0x7f); + /* Advertisement Protocol ID */ + wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len); + + /* Query Response Length */ + wpabuf_put_le16(resp, resp_frag_len); + if (!comeback_delay) + wpabuf_put_buf(resp, query_resp); + + if (comeback_delay) { + wpa_printf(MSG_DEBUG, + "GAS: Need to fragment query response"); + } else { + wpa_printf(MSG_DEBUG, + "GAS: Full query response fits in the GAS Initial Response frame"); + } + response->offset = resp_frag_len; + response->resp = query_resp; + dl_list_add(&gas->responses, &response->list); + gas->tx(gas->ctx, freq, da, resp, comeback_delay ? 2000 : 0); + wpabuf_free(resp); + eloop_register_timeout(GAS_QUERY_TIMEOUT, 0, + gas_server_response_timeout, response, NULL); +} + + +static int +gas_server_rx_initial_req(struct gas_server *gas, const u8 *da, const u8 *sa, + const u8 *bssid, int freq, u8 dialog_token, + const u8 *data, size_t len) +{ + const u8 *pos, *end, *adv_proto, *query_req; + u8 adv_proto_len; + u16 query_req_len; + struct gas_server_handler *handler; + struct wpabuf *resp; + + wpa_hexdump(MSG_MSGDUMP, "GAS: Received GAS Initial Request frame", + data, len); + pos = data; + end = data + len; + + if (end - pos < 2 || pos[0] != WLAN_EID_ADV_PROTO) { + wpa_printf(MSG_DEBUG, + "GAS: No Advertisement Protocol element found"); + return -1; + } + pos++; + adv_proto_len = *pos++; + if (end - pos < adv_proto_len || adv_proto_len < 2) { + wpa_printf(MSG_DEBUG, + "GAS: Truncated Advertisement Protocol element"); + return -1; + } + + adv_proto = pos; + pos += adv_proto_len; + wpa_hexdump(MSG_MSGDUMP, "GAS: Advertisement Protocol element", + adv_proto, adv_proto_len); + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "GAS: No Query Request Length field"); + return -1; + } + query_req_len = WPA_GET_LE16(pos); + pos += 2; + if (end - pos < query_req_len) { + wpa_printf(MSG_DEBUG, "GAS: Truncated Query Request field"); + return -1; + } + query_req = pos; + pos += query_req_len; + wpa_hexdump(MSG_MSGDUMP, "GAS: Query Request", + query_req, query_req_len); + + if (pos < end) { + wpa_hexdump(MSG_MSGDUMP, + "GAS: Ignored extra data after Query Request field", + pos, end - pos); + } + + dl_list_for_each(handler, &gas->handlers, struct gas_server_handler, + list) { + if (adv_proto_len < 1 + handler->adv_proto_id_len || + os_memcmp(adv_proto + 1, handler->adv_proto_id, + handler->adv_proto_id_len) != 0) + continue; + + wpa_printf(MSG_DEBUG, + "GAS: Calling handler for the requested Advertisement Protocol ID"); + resp = handler->req_cb(handler->ctx, sa, query_req, + query_req_len); + wpa_hexdump_buf(MSG_MSGDUMP, "GAS: Response from the handler", + resp); + gas_server_send_resp(gas, handler, sa, freq, dialog_token, + resp); + return 0; + } + + wpa_printf(MSG_DEBUG, + "GAS: No registered handler for the requested Advertisement Protocol ID"); + return -1; +} + + +static void +gas_server_handle_rx_comeback_req(struct gas_server_response *response) +{ + struct gas_server_handler *handler = response->handler; + struct gas_server *gas = handler->gas; + size_t max_len = (response->freq > 56160) ? 928 : 1400; + size_t hdr_len = 24 + 2 + 6 + 3 + handler->adv_proto_id_len + 2; + size_t remaining, resp_frag_len; + struct wpabuf *resp; + + remaining = wpabuf_len(response->resp) - response->offset; + if (hdr_len + remaining > max_len) + resp_frag_len = max_len - hdr_len; + else + resp_frag_len = remaining; + wpa_printf(MSG_DEBUG, + "GAS: Sending out %u/%u remaining Query Response octets", + (unsigned int) resp_frag_len, (unsigned int) remaining); + + resp = gas_build_comeback_resp(response->dialog_token, + WLAN_STATUS_SUCCESS, + response->frag_id++, + resp_frag_len < remaining, 0, + handler->adv_proto_id_len + + resp_frag_len); + if (!resp) { + dl_list_del(&response->list); + gas_server_free_response(response); + return; + } + + /* Advertisement Protocol element */ + wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */ + wpabuf_put_u8(resp, 0x7f); + /* Advertisement Protocol ID */ + wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len); + + /* Query Response Length */ + wpabuf_put_le16(resp, resp_frag_len); + wpabuf_put_data(resp, wpabuf_head_u8(response->resp) + response->offset, + resp_frag_len); + + response->offset += resp_frag_len; + + gas->tx(gas->ctx, response->freq, response->dst, resp, + remaining > resp_frag_len ? 2000 : 0); + wpabuf_free(resp); +} + + +static int +gas_server_rx_comeback_req(struct gas_server *gas, const u8 *da, const u8 *sa, + const u8 *bssid, int freq, u8 dialog_token) +{ + struct gas_server_response *response; + + dl_list_for_each(response, &gas->responses, struct gas_server_response, + list) { + if (response->dialog_token != dialog_token || + os_memcmp(sa, response->dst, ETH_ALEN) != 0) + continue; + gas_server_handle_rx_comeback_req(response); + return 0; + } + + wpa_printf(MSG_DEBUG, "GAS: No pending GAS response for " MACSTR + " (dialog token %u)", MAC2STR(sa), dialog_token); + return -1; +} + + +/** + * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame + * @gas: GAS query data from gas_server_init() + * @da: Destination MAC address of the Action frame + * @sa: Source MAC address of the Action frame + * @bssid: BSSID of the Action frame + * @categ: Category of the Action frame + * @data: Payload of the Action frame + * @len: Length of @data + * @freq: Frequency (in MHz) on which the frame was received + * Returns: 0 if the Public Action frame was a GAS request frame or -1 if not + */ +int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa, + const u8 *bssid, u8 categ, const u8 *data, size_t len, + int freq) +{ + u8 action, dialog_token; + const u8 *pos, *end; + + if (!gas || len < 2) + return -1; + + if (categ == WLAN_ACTION_PROTECTED_DUAL) + return -1; /* Not supported for now */ + + pos = data; + end = data + len; + action = *pos++; + dialog_token = *pos++; + + if (action != WLAN_PA_GAS_INITIAL_REQ && + action != WLAN_PA_GAS_COMEBACK_REQ) + return -1; /* Not a GAS request */ + + wpa_printf(MSG_DEBUG, "GAS: Received GAS %s Request frame DA=" MACSTR + " SA=" MACSTR " BSSID=" MACSTR + " freq=%d dialog_token=%u len=%u", + action == WLAN_PA_GAS_INITIAL_REQ ? "Initial" : "Comeback", + MAC2STR(da), MAC2STR(sa), MAC2STR(bssid), freq, dialog_token, + (unsigned int) len); + + if (action == WLAN_PA_GAS_INITIAL_REQ) + return gas_server_rx_initial_req(gas, da, sa, bssid, + freq, dialog_token, + pos, end - pos); + return gas_server_rx_comeback_req(gas, da, sa, bssid, + freq, dialog_token); +} + + +static void gas_server_handle_tx_status(struct gas_server_response *response, + int ack) +{ + if (ack && response->offset < wpabuf_len(response->resp)) { + wpa_printf(MSG_DEBUG, + "GAS: More fragments remaining - keep pending entry"); + return; + } + + if (!ack) + wpa_printf(MSG_DEBUG, + "GAS: No ACK received - drop pending entry"); + else + wpa_printf(MSG_DEBUG, + "GAS: Last fragment of the response sent out - drop pending entry"); + + response->handler->status_cb(response->handler->ctx, + response->resp, ack); + response->resp = NULL; + dl_list_del(&response->list); + gas_server_free_response(response); +} + + +void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data, + size_t data_len, int ack) +{ + const u8 *pos; + u8 action, code, dialog_token; + struct gas_server_response *response; + + if (data_len < 24 + 3) + return; + pos = data + 24; + action = *pos++; + code = *pos++; + dialog_token = *pos++; + if (action != WLAN_ACTION_PUBLIC || + (code != WLAN_PA_GAS_INITIAL_RESP && + code != WLAN_PA_GAS_COMEBACK_RESP)) + return; + wpa_printf(MSG_DEBUG, "GAS: TX status dst=" MACSTR + " ack=%d %s dialog_token=%u", + MAC2STR(dst), ack, + code == WLAN_PA_GAS_INITIAL_RESP ? "initial" : "comeback", + dialog_token); + dl_list_for_each(response, &gas->responses, struct gas_server_response, + list) { + if (response->dialog_token != dialog_token || + os_memcmp(dst, response->dst, ETH_ALEN) != 0) + continue; + gas_server_handle_tx_status(response, ack); + return; + } + + wpa_printf(MSG_DEBUG, "GAS: No pending response matches TX status"); +} + + +struct gas_server * gas_server_init(void *ctx, + void (*tx)(void *ctx, int freq, + const u8 *da, + struct wpabuf *buf, + unsigned int wait_time)) +{ + struct gas_server *gas; + + gas = os_zalloc(sizeof(*gas)); + if (!gas) + return NULL; + gas->ctx = ctx; + gas->tx = tx; + dl_list_init(&gas->handlers); + dl_list_init(&gas->responses); + return gas; +} + + +void gas_server_deinit(struct gas_server *gas) +{ + struct gas_server_handler *handler, *tmp; + struct gas_server_response *response, *tmp_r; + + if (!gas) + return; + + dl_list_for_each_safe(handler, tmp, &gas->handlers, + struct gas_server_handler, list) { + dl_list_del(&handler->list); + os_free(handler); + } + + dl_list_for_each_safe(response, tmp_r, &gas->responses, + struct gas_server_response, list) { + dl_list_del(&response->list); + gas_server_free_response(response); + } + + os_free(gas); +} + + +int gas_server_register(struct gas_server *gas, + const u8 *adv_proto_id, u8 adv_proto_id_len, + struct wpabuf * + (*req_cb)(void *ctx, const u8 *sa, + const u8 *query, size_t query_len), + void (*status_cb)(void *ctx, struct wpabuf *resp, + int ok), + void *ctx) +{ + struct gas_server_handler *handler; + + if (!gas || adv_proto_id_len > MAX_ADV_PROTO_ID_LEN) + return -1; + handler = os_zalloc(sizeof(*handler)); + if (!handler) + return -1; + + os_memcpy(handler->adv_proto_id, adv_proto_id, adv_proto_id_len); + handler->adv_proto_id_len = adv_proto_id_len; + handler->req_cb = req_cb; + handler->status_cb = status_cb; + handler->ctx = ctx; + handler->gas = gas; + dl_list_add(&gas->handlers, &handler->list); + + return 0; +} diff --git a/src/common/gas_server.h b/src/common/gas_server.h new file mode 100644 index 000000000000..299f529f7c56 --- /dev/null +++ b/src/common/gas_server.h @@ -0,0 +1,44 @@ +/* + * Generic advertisement service (GAS) server + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef GAS_SERVER_H +#define GAS_SERVER_H + +#ifdef CONFIG_GAS_SERVER + +struct gas_server; + +struct gas_server * gas_server_init(void *ctx, + void (*tx)(void *ctx, int freq, + const u8 *da, + struct wpabuf *buf, + unsigned int wait_time)); +void gas_server_deinit(struct gas_server *gas); +int gas_server_register(struct gas_server *gas, + const u8 *adv_proto_id, u8 adv_proto_id_len, + struct wpabuf * + (*req_cb)(void *ctx, const u8 *sa, + const u8 *query, size_t query_len), + void (*status_cb)(void *ctx, struct wpabuf *resp, + int ok), + void *ctx); +int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa, + const u8 *bssid, u8 categ, const u8 *data, size_t len, + int freq); +void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data, + size_t data_len, int ack); + +#else /* CONFIG_GAS_SERVER */ + +static inline void gas_server_deinit(struct gas_server *gas) +{ +} + +#endif /* CONFIG_GAS_SERVER */ + +#endif /* GAS_SERVER_H */ diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c index 9c37ea63ca87..db4037927185 100644 --- a/src/common/hw_features_common.c +++ b/src/common/hw_features_common.c @@ -89,7 +89,7 @@ int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan, { int ok, j, first; int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140, - 149, 157, 184, 192 }; + 149, 157, 165, 184, 192 }; size_t k; if (pri_chan == sec_chan || !sec_chan) @@ -388,8 +388,10 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, /* fall through */ case VHT_CHANWIDTH_80MHZ: data->bandwidth = 80; - if ((vht_oper_chwidth == 1 && center_segment1) || - (vht_oper_chwidth == 3 && !center_segment1) || + if ((vht_oper_chwidth == VHT_CHANWIDTH_80MHZ && + center_segment1) || + (vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ && + !center_segment1) || !sec_channel_offset) return -1; if (!center_segment0) { @@ -453,3 +455,101 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, return 0; } + + +void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps, + int disabled) +{ + /* Masking these out disables HT40 */ + le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET | + HT_CAP_INFO_SHORT_GI40MHZ); + + if (disabled) + htcaps->ht_capabilities_info &= ~msk; + else + htcaps->ht_capabilities_info |= msk; +} + + +#ifdef CONFIG_IEEE80211AC + +static int _ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, + const char *name) +{ + u32 req_cap = conf & cap; + + /* + * Make sure we support all requested capabilities. + * NOTE: We assume that 'cap' represents a capability mask, + * not a discrete value. + */ + if ((hw & req_cap) != req_cap) { + wpa_printf(MSG_ERROR, + "Driver does not support configured VHT capability [%s]", + name); + return 0; + } + return 1; +} + + +static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask, + unsigned int shift, + const char *name) +{ + u32 hw_max = hw & mask; + u32 conf_val = conf & mask; + + if (conf_val > hw_max) { + wpa_printf(MSG_ERROR, + "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)", + name, conf_val >> shift, hw_max >> shift); + return 0; + } + return 1; +} + + +int ieee80211ac_cap_check(u32 hw, u32 conf) +{ +#define VHT_CAP_CHECK(cap) \ + do { \ + if (!_ieee80211ac_cap_check(hw, conf, cap, #cap)) \ + return 0; \ + } while (0) + +#define VHT_CAP_CHECK_MAX(cap) \ + do { \ + if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \ + #cap)) \ + return 0; \ + } while (0) + + VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK); + VHT_CAP_CHECK_MAX(VHT_CAP_SUPP_CHAN_WIDTH_MASK); + VHT_CAP_CHECK(VHT_CAP_RXLDPC); + VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80); + VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160); + VHT_CAP_CHECK(VHT_CAP_TXSTBC); + VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK); + VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE); + VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE); + VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX); + VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX); + VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE); + VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE); + VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS); + VHT_CAP_CHECK(VHT_CAP_HTC_VHT); + VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX); + VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB); + VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB); + VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN); + VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN); + +#undef VHT_CAP_CHECK +#undef VHT_CAP_CHECK_MAX + + return 1; +} + +#endif /* CONFIG_IEEE80211AC */ diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h index 7360b4e3efed..9cddbd50e56a 100644 --- a/src/common/hw_features_common.h +++ b/src/common/hw_features_common.h @@ -35,5 +35,8 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, int vht_enabled, int sec_channel_offset, int vht_oper_chwidth, int center_segment0, int center_segment1, u32 vht_caps); +void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps, + int disabled); +int ieee80211ac_cap_check(u32 hw, u32 conf); #endif /* HW_FEATURES_COMMON_H */ diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index b6bc449bf7dc..e1ef27795b99 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -11,6 +11,7 @@ #include "common.h" #include "defs.h" #include "wpa_common.h" +#include "drivers/driver.h" #include "qca-vendor.h" #include "ieee802_11_defs.h" #include "ieee802_11_common.h" @@ -120,6 +121,11 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->mbo = pos; elems->mbo_len = elen; break; + case HS20_ROAMING_CONS_SEL_OUI_TYPE: + /* Hotspot 2.0 Roaming Consortium Selection */ + elems->roaming_cons_sel = pos; + elems->roaming_cons_sel_len = elen; + break; default: wpa_printf(MSG_MSGDUMP, "Unknown WFA " "information element ignored " @@ -179,6 +185,100 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, } +static int ieee802_11_parse_extension(const u8 *pos, size_t elen, + struct ieee802_11_elems *elems, + int show_errors) +{ + u8 ext_id; + + if (elen < 1) { + if (show_errors) { + wpa_printf(MSG_MSGDUMP, + "short information element (Ext)"); + } + return -1; + } + + ext_id = *pos++; + elen--; + + switch (ext_id) { + case WLAN_EID_EXT_ASSOC_DELAY_INFO: + if (elen != 1) + break; + elems->assoc_delay_info = pos; + break; + case WLAN_EID_EXT_FILS_REQ_PARAMS: + if (elen < 3) + break; + elems->fils_req_params = pos; + elems->fils_req_params_len = elen; + break; + case WLAN_EID_EXT_FILS_KEY_CONFIRM: + elems->fils_key_confirm = pos; + elems->fils_key_confirm_len = elen; + break; + case WLAN_EID_EXT_FILS_SESSION: + if (elen != FILS_SESSION_LEN) + break; + elems->fils_session = pos; + break; + case WLAN_EID_EXT_FILS_HLP_CONTAINER: + if (elen < 2 * ETH_ALEN) + break; + elems->fils_hlp = pos; + elems->fils_hlp_len = elen; + break; + case WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN: + if (elen < 1) + break; + elems->fils_ip_addr_assign = pos; + elems->fils_ip_addr_assign_len = elen; + break; + case WLAN_EID_EXT_KEY_DELIVERY: + if (elen < WPA_KEY_RSC_LEN) + break; + elems->key_delivery = pos; + elems->key_delivery_len = elen; + break; + case WLAN_EID_EXT_FILS_WRAPPED_DATA: + elems->fils_wrapped_data = pos; + elems->fils_wrapped_data_len = elen; + break; + case WLAN_EID_EXT_FILS_PUBLIC_KEY: + if (elen < 1) + break; + elems->fils_pk = pos; + elems->fils_pk_len = elen; + break; + case WLAN_EID_EXT_FILS_NONCE: + if (elen != FILS_NONCE_LEN) + break; + elems->fils_nonce = pos; + break; + case WLAN_EID_EXT_OWE_DH_PARAM: + if (elen < 2) + break; + elems->owe_dh = pos; + elems->owe_dh_len = elen; + break; + case WLAN_EID_EXT_PASSWORD_IDENTIFIER: + elems->password_id = pos; + elems->password_id_len = elen; + break; + default: + if (show_errors) { + wpa_printf(MSG_MSGDUMP, + "IEEE 802.11 element parsing ignored unknown element extension (ext_id=%u elen=%u)", + ext_id, (unsigned int) elen); + } + return -1; + } + + return 0; +} + + /** * ieee802_11_parse_elems - Parse information elements in management frames * @start: Pointer to the start of IEs @@ -262,6 +362,10 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->rsn_ie_len = elen; break; case WLAN_EID_PWR_CAPABILITY: + if (elen < 2) + break; + elems->power_capab = pos; + elems->power_capab_len = elen; break; case WLAN_EID_SUPPORTED_CHANNELS: elems->supp_channels = pos; @@ -379,6 +483,35 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->rrm_enabled = pos; elems->rrm_enabled_len = elen; break; + case WLAN_EID_CAG_NUMBER: + elems->cag_number = pos; + elems->cag_number_len = elen; + break; + case WLAN_EID_AP_CSN: + if (elen < 1) + break; + elems->ap_csn = pos; + break; + case WLAN_EID_FILS_INDICATION: + if (elen < 2) + break; + elems->fils_indic = pos; + elems->fils_indic_len = elen; + break; + case WLAN_EID_DILS: + if (elen < 2) + break; + elems->dils = pos; + elems->dils_len = elen; + break; + case WLAN_EID_FRAGMENT: + /* TODO */ + break; + case WLAN_EID_EXTENSION: + if (ieee802_11_parse_extension(pos, elen, elems, + show_errors)) + unknown++; + break; default: unknown++; if (!show_errors) @@ -681,6 +814,25 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, return HOSTAPD_MODE_IEEE80211A; } + /* 5 GHz, channels 52..64 */ + if (freq >= 5260 && freq <= 5320) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + + if (vht_opclass) + *op_class = vht_opclass; + else if (sec_channel == 1) + *op_class = 119; + else if (sec_channel == -1) + *op_class = 120; + else + *op_class = 118; + + *channel = (freq - 5000) / 5; + + return HOSTAPD_MODE_IEEE80211A; + } + /* 5 GHz, channels 149..169 */ if (freq >= 5745 && freq <= 5845) { if ((freq - 5000) % 5) @@ -981,7 +1133,7 @@ static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan) return -1; return 5000 + 5 * chan; case 129: /* center freqs 50, 114; 160 MHz */ - if (chan < 50 || chan > 114) + if (chan < 36 || chan > 128) return -1; return 5000 + 5 * chan; case 180: /* 60 GHz band, channels 1..4 */ @@ -1031,10 +1183,24 @@ int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan) } -int ieee80211_is_dfs(int freq) +int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes, + u16 num_modes) { - /* TODO: this could be more accurate to better cover all domains */ - return (freq >= 5260 && freq <= 5320) || (freq >= 5500 && freq <= 5700); + int i, j; + + if (!modes || !num_modes) + return (freq >= 5260 && freq <= 5320) || + (freq >= 5500 && freq <= 5700); + + for (i = 0; i < num_modes; i++) { + for (j = 0; j < modes[i].num_channels; j++) { + if (modes[i].channels[j].freq == freq && + (modes[i].channels[j].flag & HOSTAPD_CHAN_RADAR)) + return 1; + } + } + + return 0; } @@ -1295,6 +1461,40 @@ const u8 * get_ie(const u8 *ies, size_t len, u8 eid) } +/** + * get_ie_ext - Fetch a specified extended information element from IEs buffer + * @ies: Information elements buffer + * @len: Information elements buffer length + * @ext: Information element extension identifier (WLAN_EID_EXT_*) + * Returns: Pointer to the information element (id field) or %NULL if not found + * + * This function returns the first matching information element in the IEs + * buffer or %NULL in case the element is not found. + */ +const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext) +{ + const u8 *end; + + if (!ies) + return NULL; + + end = ies + len; + + while (end - ies > 1) { + if (2 + ies[1] > end - ies) + break; + + if (ies[0] == WLAN_EID_EXTENSION && ies[1] >= 1 && + ies[2] == ext) + return ies; + + ies += 2 + ies[1]; + } + + return NULL; +} + + size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len) { /* @@ -1317,3 +1517,250 @@ size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len) return 6 + attr_len; } + + +static const struct country_op_class us_op_class[] = { + { 1, 115 }, + { 2, 118 }, + { 3, 124 }, + { 4, 121 }, + { 5, 125 }, + { 12, 81 }, + { 22, 116 }, + { 23, 119 }, + { 24, 122 }, + { 25, 126 }, + { 26, 126 }, + { 27, 117 }, + { 28, 120 }, + { 29, 123 }, + { 30, 127 }, + { 31, 127 }, + { 32, 83 }, + { 33, 84 }, + { 34, 180 }, +}; + +static const struct country_op_class eu_op_class[] = { + { 1, 115 }, + { 2, 118 }, + { 3, 121 }, + { 4, 81 }, + { 5, 116 }, + { 6, 119 }, + { 7, 122 }, + { 8, 117 }, + { 9, 120 }, + { 10, 123 }, + { 11, 83 }, + { 12, 84 }, + { 17, 125 }, + { 18, 180 }, +}; + +static const struct country_op_class jp_op_class[] = { + { 1, 115 }, + { 30, 81 }, + { 31, 82 }, + { 32, 118 }, + { 33, 118 }, + { 34, 121 }, + { 35, 121 }, + { 36, 116 }, + { 37, 119 }, + { 38, 119 }, + { 39, 122 }, + { 40, 122 }, + { 41, 117 }, + { 42, 120 }, + { 43, 120 }, + { 44, 123 }, + { 45, 123 }, + { 56, 83 }, + { 57, 84 }, + { 58, 121 }, + { 59, 180 }, +}; + +static const struct country_op_class cn_op_class[] = { + { 1, 115 }, + { 2, 118 }, + { 3, 125 }, + { 4, 116 }, + { 5, 119 }, + { 6, 126 }, + { 7, 81 }, + { 8, 83 }, + { 9, 84 }, +}; + +static u8 +global_op_class_from_country_array(u8 op_class, size_t array_size, + const struct country_op_class *country_array) +{ + size_t i; + + for (i = 0; i < array_size; i++) { + if (country_array[i].country_op_class == op_class) + return country_array[i].global_op_class; + } + + return 0; +} + + +u8 country_to_global_op_class(const char *country, u8 op_class) +{ + const struct country_op_class *country_array; + size_t size; + u8 g_op_class; + + if (country_match(us_op_class_cc, country)) { + country_array = us_op_class; + size = ARRAY_SIZE(us_op_class); + } else if (country_match(eu_op_class_cc, country)) { + country_array = eu_op_class; + size = ARRAY_SIZE(eu_op_class); + } else if (country_match(jp_op_class_cc, country)) { + country_array = jp_op_class; + size = ARRAY_SIZE(jp_op_class); + } else if (country_match(cn_op_class_cc, country)) { + country_array = cn_op_class; + size = ARRAY_SIZE(cn_op_class); + } else { + /* + * Countries that do not match any of the above countries use + * global operating classes + */ + return op_class; + } + + g_op_class = global_op_class_from_country_array(op_class, size, + country_array); + + /* + * If the given operating class did not match any of the country's + * operating classes, assume that global operating class is used. + */ + return g_op_class ? g_op_class : op_class; +} + + +const struct oper_class_map * get_oper_class(const char *country, u8 op_class) +{ + const struct oper_class_map *op; + + if (country) + op_class = country_to_global_op_class(country, op_class); + + op = &global_op_class[0]; + while (op->op_class && op->op_class != op_class) + op++; + + if (!op->op_class) + return NULL; + + return op; +} + + +int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep, + size_t nei_rep_len) +{ + u8 *nei_pos = nei_rep; + const char *end; + + /* + * BSS Transition Candidate List Entries - Neighbor Report elements + * neighbor=<BSSID>,<BSSID Information>,<Operating Class>, + * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>] + */ + while (pos) { + u8 *nei_start; + long int val; + char *endptr, *tmp; + + pos = os_strstr(pos, " neighbor="); + if (!pos) + break; + if (nei_pos + 15 > nei_rep + nei_rep_len) { + wpa_printf(MSG_DEBUG, + "Not enough room for additional neighbor"); + return -1; + } + pos += 10; + + nei_start = nei_pos; + *nei_pos++ = WLAN_EID_NEIGHBOR_REPORT; + nei_pos++; /* length to be filled in */ + + if (hwaddr_aton(pos, nei_pos)) { + wpa_printf(MSG_DEBUG, "Invalid BSSID"); + return -1; + } + nei_pos += ETH_ALEN; + pos += 17; + if (*pos != ',') { + wpa_printf(MSG_DEBUG, "Missing BSSID Information"); + return -1; + } + pos++; + + val = strtol(pos, &endptr, 0); + WPA_PUT_LE32(nei_pos, val); + nei_pos += 4; + if (*endptr != ',') { + wpa_printf(MSG_DEBUG, "Missing Operating Class"); + return -1; + } + pos = endptr + 1; + + *nei_pos++ = atoi(pos); /* Operating Class */ + pos = os_strchr(pos, ','); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "Missing Channel Number"); + return -1; + } + pos++; + + *nei_pos++ = atoi(pos); /* Channel Number */ + pos = os_strchr(pos, ','); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "Missing PHY Type"); + return -1; + } + pos++; + + *nei_pos++ = atoi(pos); /* PHY Type */ + end = os_strchr(pos, ' '); + tmp = os_strchr(pos, ','); + if (tmp && (!end || tmp < end)) { + /* Optional Subelements (hexdump) */ + size_t len; + + pos = tmp + 1; + end = os_strchr(pos, ' '); + if (end) + len = end - pos; + else + len = os_strlen(pos); + if (nei_pos + len / 2 > nei_rep + nei_rep_len) { + wpa_printf(MSG_DEBUG, + "Not enough room for neighbor subelements"); + return -1; + } + if (len & 0x01 || + hexstr2bin(pos, nei_pos, len / 2) < 0) { + wpa_printf(MSG_DEBUG, + "Invalid neighbor subelement info"); + return -1; + } + nei_pos += len / 2; + pos = end; + } + + nei_start[1] = nei_pos - nei_start - 2; + } + + return nei_pos - nei_rep; +} diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index 42f39096f86c..ff7e51de3dc9 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -11,6 +11,8 @@ #include "defs.h" +struct hostapd_hw_modes; + #define MAX_NOF_MB_IES_SUPPORTED 5 struct mb_ies_info { @@ -64,6 +66,24 @@ struct ieee802_11_elems { const u8 *pref_freq_list; const u8 *supp_op_classes; const u8 *rrm_enabled; + const u8 *cag_number; + const u8 *ap_csn; + const u8 *fils_indic; + const u8 *dils; + const u8 *assoc_delay_info; + const u8 *fils_req_params; + const u8 *fils_key_confirm; + const u8 *fils_session; + const u8 *fils_hlp; + const u8 *fils_ip_addr_assign; + const u8 *key_delivery; + const u8 *fils_wrapped_data; + const u8 *fils_pk; + const u8 *fils_nonce; + const u8 *owe_dh; + const u8 *power_capab; + const u8 *roaming_cons_sel; + const u8 *password_id; u8 ssid_len; u8 supp_rates_len; @@ -96,6 +116,20 @@ struct ieee802_11_elems { u8 pref_freq_list_len; u8 supp_op_classes_len; u8 rrm_enabled_len; + u8 cag_number_len; + u8 fils_indic_len; + u8 dils_len; + u8 fils_req_params_len; + u8 fils_key_confirm_len; + u8 fils_hlp_len; + u8 fils_ip_addr_assign_len; + u8 key_delivery_len; + u8 fils_wrapped_data_len; + u8 fils_pk_len; + u8 owe_dh_len; + u8 power_capab_len; + u8 roaming_cons_sel_len; + u8 password_id_len; struct mb_ies_info mb_ies; }; @@ -126,7 +160,8 @@ int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan); enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, int sec_channel, int vht, u8 *op_class, u8 *channel); -int ieee80211_is_dfs(int freq); +int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes, + u16 num_modes); enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht); int supp_rates_11b_only(struct ieee802_11_elems *elems); @@ -150,7 +185,20 @@ extern const struct oper_class_map global_op_class[]; extern size_t global_op_class_size; const u8 * get_ie(const u8 *ies, size_t len, u8 eid); +const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext); size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len); +struct country_op_class { + u8 country_op_class; + u8 global_op_class; +}; + +u8 country_to_global_op_class(const char *country, u8 op_class); + +const struct oper_class_map * get_oper_class(const char *country, u8 op_class); + +int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep, + size_t nei_rep_len); + #endif /* IEEE802_11_COMMON_H */ diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index d453aec790ad..762e731ab022 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -81,6 +81,9 @@ #define WLAN_AUTH_SHARED_KEY 1 #define WLAN_AUTH_FT 2 #define WLAN_AUTH_SAE 3 +#define WLAN_AUTH_FILS_SK 4 +#define WLAN_AUTH_FILS_SK_PFS 5 +#define WLAN_AUTH_FILS_PK 6 #define WLAN_AUTH_LEAP 128 #define WLAN_AUTH_CHALLENGE_LEN 128 @@ -102,7 +105,7 @@ #define WLAN_CAPABILITY_DELAYED_BLOCK_ACK BIT(14) #define WLAN_CAPABILITY_IMM_BLOCK_ACK BIT(15) -/* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */ +/* Status codes (IEEE Std 802.11-2016, 9.4.1.9, Table 9-46) */ #define WLAN_STATUS_SUCCESS 0 #define WLAN_STATUS_UNSPECIFIED_FAILURE 1 #define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2 @@ -119,27 +122,23 @@ #define WLAN_STATUS_AUTH_TIMEOUT 16 #define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 #define WLAN_STATUS_ASSOC_DENIED_RATES 18 -/* IEEE 802.11b */ #define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 -#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20 -#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21 -/* IEEE 802.11h */ #define WLAN_STATUS_SPEC_MGMT_REQUIRED 22 #define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23 #define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24 -/* IEEE 802.11g */ #define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25 -#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 26 #define WLAN_STATUS_ASSOC_DENIED_NO_HT 27 #define WLAN_STATUS_R0KH_UNREACHABLE 28 #define WLAN_STATUS_ASSOC_DENIED_NO_PCO 29 -/* IEEE 802.11w */ #define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30 #define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31 #define WLAN_STATUS_UNSPECIFIED_QOS_FAILURE 32 +#define WLAN_STATUS_DENIED_INSUFFICIENT_BANDWIDTH 33 +#define WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS 34 +#define WLAN_STATUS_DENIED_QOS_NOT_SUPPORTED 35 #define WLAN_STATUS_REQUEST_DECLINED 37 #define WLAN_STATUS_INVALID_PARAMETERS 38 -/* IEEE 802.11i */ +#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_CHANGES 39 #define WLAN_STATUS_INVALID_IE 40 #define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41 #define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42 @@ -152,11 +151,13 @@ #define WLAN_STATUS_DEST_STA_NOT_PRESENT 49 #define WLAN_STATUS_DEST_STA_NOT_QOS_STA 50 #define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51 -/* IEEE 802.11r */ #define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52 #define WLAN_STATUS_INVALID_PMKID 53 #define WLAN_STATUS_INVALID_MDIE 54 #define WLAN_STATUS_INVALID_FTIE 55 +#define WLAN_STATUS_REQUESTED_TCLAS_NOT_SUPPORTED 56 +#define WLAN_STATUS_INSUFFICIENT_TCLAS_PROCESSING_RESOURCES 57 +#define WLAN_STATUS_TRY_ANOTHER_BSS 58 #define WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED 59 #define WLAN_STATUS_NO_OUTSTANDING_GAS_REQ 60 #define WLAN_STATUS_GAS_RESP_NOT_RECEIVED 61 @@ -167,16 +168,44 @@ #define WLAN_STATUS_REQ_REFUSED_SSPN 67 #define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68 #define WLAN_STATUS_INVALID_RSNIE 72 +#define WLAN_STATUS_U_APSD_COEX_NOT_SUPPORTED 73 +#define WLAN_STATUS_U_APSD_COEX_MODE_NOT_SUPPORTED 74 +#define WLAN_STATUS_BAD_INTERVAL_WITH_U_APSD_COEX 75 #define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76 #define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77 +#define WLAN_STATUS_CANNOT_FIND_ALT_TBTT 78 #define WLAN_STATUS_TRANSMISSION_FAILURE 79 +#define WLAN_STATUS_REQ_TCLAS_NOT_SUPPORTED 80 +#define WLAN_STATUS_TCLAS_RESOURCES_EXCHAUSTED 81 #define WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION 82 +#define WLAN_STATUS_REJECT_WITH_SCHEDULE 83 +#define WLAN_STATUS_REJECT_NO_WAKEUP_SPECIFIED 84 +#define WLAN_STATUS_SUCCESS_POWER_SAVE_MODE 85 #define WLAN_STATUS_PENDING_ADMITTING_FST_SESSION 86 +#define WLAN_STATUS_PERFORMING_FST_NOW 87 +#define WLAN_STATUS_PENDING_GAP_IN_BA_WINDOW 88 +#define WLAN_STATUS_REJECT_U_PID_SETTING 89 +#define WLAN_STATUS_REFUSED_EXTERNAL_REASON 92 +#define WLAN_STATUS_REFUSED_AP_OUT_OF_MEMORY 93 +#define WLAN_STATUS_REJECTED_EMERGENCY_SERVICE_NOT_SUPPORTED 94 #define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95 +#define WLAN_STATUS_REJECT_DSE_BAND 96 +#define WLAN_STATUS_TCLAS_PROCESSING_TERMINATED 97 +#define WLAN_STATUS_TS_SCHEDULE_CONFLICT 98 #define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99 +#define WLAN_STATUS_MCCAOP_RESERVATION_CONFLICT 100 +#define WLAN_STATUS_MAF_LIMIT_EXCEEDED 101 +#define WLAN_STATUS_MCCA_TRACK_LIMIT_EXCEEDED 102 +#define WLAN_STATUS_DENIED_DUE_TO_SPECTRUM_MANAGEMENT 103 #define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104 +#define WLAN_STATUS_ENABLEMENT_DENIED 105 +#define WLAN_STATUS_RESTRICTION_FROM_AUTHORIZED_GDB 106 +#define WLAN_STATUS_AUTHORIZATION_DEENABLED 107 +#define WLAN_STATUS_FILS_AUTHENTICATION_FAILURE 112 +#define WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER 113 +#define WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER 123 -/* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */ +/* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */ #define WLAN_REASON_UNSPECIFIED 1 #define WLAN_REASON_PREV_AUTH_NOT_VALID 2 #define WLAN_REASON_DEAUTH_LEAVING 3 @@ -186,10 +215,9 @@ #define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7 #define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8 #define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 -/* IEEE 802.11h */ #define WLAN_REASON_PWR_CAPABILITY_NOT_VALID 10 #define WLAN_REASON_SUPPORTED_CHANNEL_NOT_VALID 11 -/* IEEE 802.11i */ +#define WLAN_REASON_BSS_TRANSITION_DISASSOC 12 #define WLAN_REASON_INVALID_IE 13 #define WLAN_REASON_MICHAEL_MIC_FAILURE 14 #define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15 @@ -204,9 +232,26 @@ #define WLAN_REASON_CIPHER_SUITE_REJECTED 24 #define WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE 25 #define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26 -/* IEEE 802.11e */ +#define WLAN_REASON_SSP_REQUESTED_DISASSOC 27 +#define WLAN_REASON_NO_SSP_ROAMING_AGREEMENT 28 +#define WLAN_REASON_BAD_CIPHER_OR_AKM 29 +#define WLAN_REASON_NOT_AUTHORIZED_THIS_LOCATION 30 +#define WLAN_REASON_SERVICE_CHANGE_PRECLUDES_TS 31 +#define WLAN_REASON_UNSPECIFIED_QOS_REASON 32 +#define WLAN_REASON_NOT_ENOUGH_BANDWIDTH 33 #define WLAN_REASON_DISASSOC_LOW_ACK 34 -/* IEEE 802.11s */ +#define WLAN_REASON_EXCEEDED_TXOP 35 +#define WLAN_REASON_STA_LEAVING 36 +#define WLAN_REASON_END_TS_BA_DLS 37 +#define WLAN_REASON_UNKNOWN_TS_BA 38 +#define WLAN_REASON_TIMEOUT 39 +#define WLAN_REASON_PEERKEY_MISMATCH 45 +#define WLAN_REASON_AUTHORIZED_ACCESS_LIMIT_REACHED 46 +#define WLAN_REASON_EXTERNAL_SERVICE_REQUIREMENTS 47 +#define WLAN_REASON_INVALID_FT_ACTION_FRAME_COUNT 48 +#define WLAN_REASON_INVALID_PMKID 49 +#define WLAN_REASON_INVALID_MDE 50 +#define WLAN_REASON_INVALID_FTE 51 #define WLAN_REASON_MESH_PEERING_CANCELLED 52 #define WLAN_REASON_MESH_MAX_PEERS 53 #define WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION 54 @@ -216,20 +261,29 @@ #define WLAN_REASON_MESH_INVALID_GTK 58 #define WLAN_REASON_MESH_INCONSISTENT_PARAMS 59 #define WLAN_REASON_MESH_INVALID_SECURITY_CAP 60 +#define WLAN_REASON_MESH_PATH_ERROR_NO_PROXY_INFO 61 +#define WLAN_REASON_MESH_PATH_ERROR_NO_FORWARDING_INFO 62 +#define WLAN_REASON_MESH_PATH_ERROR_DEST_UNREACHABLE 63 +#define WLAN_REASON_MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS 64 +#define WLAN_REASON_MESH_CHANNEL_SWITCH_REGULATORY_REQ 65 +#define WLAN_REASON_MESH_CHANNEL_SWITCH_UNSPECIFIED 66 -/* Information Element IDs */ +/* Information Element IDs (IEEE Std 802.11-2016, 9.4.2.1, Table 9-77) */ #define WLAN_EID_SSID 0 #define WLAN_EID_SUPP_RATES 1 -#define WLAN_EID_FH_PARAMS 2 #define WLAN_EID_DS_PARAMS 3 #define WLAN_EID_CF_PARAMS 4 #define WLAN_EID_TIM 5 #define WLAN_EID_IBSS_PARAMS 6 #define WLAN_EID_COUNTRY 7 +#define WLAN_EID_REQUEST 10 #define WLAN_EID_BSS_LOAD 11 +#define WLAN_EID_EDCA_PARAM_SET 12 +#define WLAN_EID_TSPEC 13 +#define WLAN_EID_TCLAS 14 +#define WLAN_EID_SCHEDULE 15 #define WLAN_EID_CHALLENGE 16 -/* EIDs defined by IEEE 802.11h - START */ #define WLAN_EID_PWR_CONSTRAINT 32 #define WLAN_EID_PWR_CAPABILITY 33 #define WLAN_EID_TPC_REQUEST 34 @@ -238,50 +292,139 @@ #define WLAN_EID_CHANNEL_SWITCH 37 #define WLAN_EID_MEASURE_REQUEST 38 #define WLAN_EID_MEASURE_REPORT 39 -#define WLAN_EID_QUITE 40 +#define WLAN_EID_QUIET 40 #define WLAN_EID_IBSS_DFS 41 -/* EIDs defined by IEEE 802.11h - END */ #define WLAN_EID_ERP_INFO 42 +#define WLAN_EID_TS_DELAY 43 +#define WLAN_EID_TCLAS_PROCESSING 44 #define WLAN_EID_HT_CAP 45 #define WLAN_EID_QOS 46 #define WLAN_EID_RSN 48 #define WLAN_EID_EXT_SUPP_RATES 50 +#define WLAN_EID_AP_CHANNEL_REPORT 51 #define WLAN_EID_NEIGHBOR_REPORT 52 +#define WLAN_EID_RCPI 53 #define WLAN_EID_MOBILITY_DOMAIN 54 #define WLAN_EID_FAST_BSS_TRANSITION 55 #define WLAN_EID_TIMEOUT_INTERVAL 56 #define WLAN_EID_RIC_DATA 57 +#define WLAN_EID_DSE_REGISTERED_LOCATION 58 #define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59 #define WLAN_EID_EXT_CHANSWITCH_ANN 60 #define WLAN_EID_HT_OPERATION 61 #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62 -#define WLAN_EID_WAPI 68 +#define WLAN_EID_BSS_AVERAGE_ACCESS_DELAY 63 +#define WLAN_EID_ANTENNA 64 +#define WLAN_EID_RSNI 65 +#define WLAN_EID_MEASUREMENT_PILOT_TRANSMISSION 66 +#define WLAN_EID_BSS_AVAILABLE_ADM_CAPA 67 +#define WLAN_EID_BSS_AC_ACCESS_DELAY 68 /* note: also used by WAPI */ #define WLAN_EID_TIME_ADVERTISEMENT 69 #define WLAN_EID_RRM_ENABLED_CAPABILITIES 70 +#define WLAN_EID_MULTIPLE_BSSID 71 #define WLAN_EID_20_40_BSS_COEXISTENCE 72 #define WLAN_EID_20_40_BSS_INTOLERANT 73 #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74 +#define WLAN_EID_RIC_DESCRIPTOR 75 #define WLAN_EID_MMIE 76 +#define WLAN_EID_EVENT_REQUEST 78 +#define WLAN_EID_EVENT_REPORT 79 +#define WLAN_EID_DIAGNOSTIC_REQUEST 80 +#define WLAN_EID_DIAGNOSTIC_REPORT 81 +#define WLAN_EID_LOCATION_PARAMETERS 82 +#define WLAN_EID_NONTRANSMITTED_BSSID_CAPA 83 #define WLAN_EID_SSID_LIST 84 +#define WLAN_EID_MLTIPLE_BSSID_INDEX 85 +#define WLAN_EID_FMS_DESCRIPTOR 86 +#define WLAN_EID_FMS_REQUEST 87 +#define WLAN_EID_FMS_RESPONSE 88 +#define WLAN_EID_QOS_TRAFFIC_CAPABILITY 89 #define WLAN_EID_BSS_MAX_IDLE_PERIOD 90 #define WLAN_EID_TFS_REQ 91 #define WLAN_EID_TFS_RESP 92 #define WLAN_EID_WNMSLEEP 93 +#define WLAN_EID_TIM_BROADCAST_REQUEST 94 +#define WLAN_EID_TIM_BROADCAST_RESPONSE 95 +#define WLAN_EID_COLLOCATED_INTERFERENCE_REPORT 96 +#define WLAN_EID_CHANNEL_USAGE 97 #define WLAN_EID_TIME_ZONE 98 +#define WLAN_EID_DMS_REQUEST 99 +#define WLAN_EID_DMS_RESPONSE 100 #define WLAN_EID_LINK_ID 101 +#define WLAN_EID_WAKEUP_SCHEDULE 102 +#define WLAN_EID_CHANNEL_SWITCH_TIMING 104 +#define WLAN_EID_PTI_CONTROL 105 +#define WLAN_EID_TPU_BUFFER_STATUS 106 #define WLAN_EID_INTERWORKING 107 #define WLAN_EID_ADV_PROTO 108 +#define WLAN_EID_EXPEDITED_BANDWIDTH_REQ 109 #define WLAN_EID_QOS_MAP_SET 110 #define WLAN_EID_ROAMING_CONSORTIUM 111 +#define WLAN_EID_EMERGENCY_ALERT_ID 112 #define WLAN_EID_MESH_CONFIG 113 #define WLAN_EID_MESH_ID 114 +#define WLAN_EID_MESH_LINK_METRIC_REPORT 115 +#define WLAN_EID_CONGESTION_NOTIFICATION 116 #define WLAN_EID_PEER_MGMT 117 +#define WLAN_EID_MESH_CHANNEL_SWITCH_PARAMETERS 118 +#define WLAN_EID_MESH_AWAKE_WINDOW 119 +#define WLAN_EID_BEACON_TIMING 120 +#define WLAN_EID_MCCAOP_SETUP_REQUEST 121 +#define WLAN_EID_MCCAOP_SETUP_REPLY 122 +#define WLAN_EID_MCCAOP_ADVERTISEMENT 123 +#define WLAN_EID_MCCAOP_TEARDOWN 124 +#define WLAN_EID_GANN 125 +#define WLAN_EID_RANN 126 #define WLAN_EID_EXT_CAPAB 127 +#define WLAN_EID_PREQ 130 +#define WLAN_EID_PREP 131 +#define WLAN_EID_PERR 132 +#define WLAN_EID_PXU 137 +#define WLAN_EID_PXUC 138 #define WLAN_EID_AMPE 139 #define WLAN_EID_MIC 140 +#define WLAN_EID_DESTINATION_URI 141 +#define WLAN_EID_U_APSD_COEX 142 +#define WLAN_EID_DMG_WAKEUP_SCHEDULE 143 +#define WLAN_EID_EXTENDED_SCHEDULE 144 +#define WLAN_EID_STA_AVAILABILITY 145 +#define WLAN_EID_DMG_TSPEC 146 +#define WLAN_EID_NEXT_DMG_ATI 147 +#define WLAN_EID_DMG_CAPABILITIES 148 +#define WLAN_EID_DMG_OPERATION 151 +#define WLAN_EID_DMG_BSS_PARAMETER_CHANGE 152 +#define WLAN_EID_DMG_BEAM_REFINEMENT 153 +#define WLAN_EID_CHANNEL_MEASUREMENT_FEEDBACK 154 #define WLAN_EID_CCKM 156 +#define WLAN_EID_AWAKE_WINDOW 157 #define WLAN_EID_MULTI_BAND 158 +#define WLAN_EID_ADDBA_EXTENSION 159 +#define WLAN_EID_NEXTPCP_LIST 160 +#define WLAN_EID_PCP_HANDOVER 161 +#define WLAN_EID_DMG_LINK_MARGIN 162 +#define WLAN_EID_SWITCHING_STREAM 163 #define WLAN_EID_SESSION_TRANSITION 164 +#define WLAN_EID_DYNAMIC_TONE_PAIRING_REPORT 165 +#define WLAN_EID_CLUSTER_REPORT 166 +#define WLAN_EID_REPLAY_CAPABILITIES 167 +#define WLAN_EID_RELAY_TRANSFER_PARAM_SET 168 +#define WLAN_EID_BEAMLINK_MAINTENANCE 169 +#define WLAN_EID_MULTIPLE_MAC_SUBLAYERS 170 +#define WLAN_EID_U_PID 171 +#define WLAN_EID_DMG_LINK_ADAPTATION_ACK 172 +#define WLAN_EID_MCCAOP_ADVERTISEMENT_OVERVIEW 174 +#define WLAN_EID_QUIET_PERIOD_REQUEST 175 +#define WLAN_EID_QUIET_PERIOD_RESPONSE 177 +#define WLAN_EID_QMF_POLICY 181 +#define WLAN_EID_ECAPC_POLICY 182 +#define WLAN_EID_CLUSTER_TIME_OFFSET 183 +#define WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY 184 +#define WLAN_EID_SCS_DESCRIPTOR 185 +#define WLAN_EID_QLOAD_REPORT 186 +#define WLAN_EID_HCCA_TXOP_UPDATE_COUNT 187 +#define WLAN_EID_HIGHER_LAYER_STREAM_ID 188 +#define WLAN_EID_GCR_GROUP_ADDRESS 189 +#define WLAN_EID_ANTENNA_SECTOR_ID_PATTERN 190 #define WLAN_EID_VHT_CAP 191 #define WLAN_EID_VHT_OPERATION 192 #define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193 @@ -291,10 +434,42 @@ #define WLAN_EID_VHT_AID 197 #define WLAN_EID_VHT_QUIET_CHANNEL 198 #define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199 +#define WLAN_EID_UPSIM 200 +#define WLAN_EID_REDUCED_NEIGHBOR_REPORT 201 +#define WLAN_EID_TVHT_OPERATION 202 +#define WLAN_EID_DEVICE_LOCATION 204 +#define WLAN_EID_WHITE_SPACE_MAP 205 +#define WLAN_EID_FTM_PARAMETERS 206 #define WLAN_EID_VENDOR_SPECIFIC 221 +#define WLAN_EID_CAG_NUMBER 237 +#define WLAN_EID_AP_CSN 239 +#define WLAN_EID_FILS_INDICATION 240 +#define WLAN_EID_DILS 241 +#define WLAN_EID_FRAGMENT 242 +#define WLAN_EID_EXTENSION 255 +/* Element ID Extension (EID 255) values */ +#define WLAN_EID_EXT_ASSOC_DELAY_INFO 1 +#define WLAN_EID_EXT_FILS_REQ_PARAMS 2 +#define WLAN_EID_EXT_FILS_KEY_CONFIRM 3 +#define WLAN_EID_EXT_FILS_SESSION 4 +#define WLAN_EID_EXT_FILS_HLP_CONTAINER 5 +#define WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN 6 +#define WLAN_EID_EXT_KEY_DELIVERY 7 +#define WLAN_EID_EXT_FILS_WRAPPED_DATA 8 +#define WLAN_EID_EXT_FTM_SYNC_INFO 9 +#define WLAN_EID_EXT_EXTENDED_REQUEST 10 +#define WLAN_EID_EXT_ESTIMATED_SERVICE_PARAMS 11 +#define WLAN_EID_EXT_FILS_PUBLIC_KEY 12 +#define WLAN_EID_EXT_FILS_NONCE 13 +#define WLAN_EID_EXT_FUTURE_CHANNEL_GUIDANCE 14 +#define WLAN_EID_EXT_OWE_DH_PARAM 32 +#define WLAN_EID_EXT_PASSWORD_IDENTIFIER 33 +#define WLAN_EID_EXT_HE_CAPABILITIES 35 +#define WLAN_EID_EXT_HE_OPERATION 36 -/* Action frame categories (IEEE 802.11-2007, 7.3.1.11, Table 7-24) */ + +/* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */ #define WLAN_ACTION_SPECTRUM_MGMT 0 #define WLAN_ACTION_QOS 1 #define WLAN_ACTION_DLS 2 @@ -308,21 +483,59 @@ #define WLAN_ACTION_WNM 10 #define WLAN_ACTION_UNPROTECTED_WNM 11 #define WLAN_ACTION_TDLS 12 +#define WLAN_ACTION_MESH 13 +#define WLAN_ACTION_MULTIHOP 14 #define WLAN_ACTION_SELF_PROTECTED 15 +#define WLAN_ACTION_DMG 16 #define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */ #define WLAN_ACTION_FST 18 +#define WLAN_ACTION_ROBUST_AV_STREAMING 19 +#define WLAN_ACTION_UNPROTECTED_DMG 20 +#define WLAN_ACTION_VHT 21 +#define WLAN_ACTION_FILS 26 +#define WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED 126 #define WLAN_ACTION_VENDOR_SPECIFIC 127 +/* Note: 128-255 used to report errors by setting category | 0x80 */ -/* Public action codes */ +/* Public action codes (IEEE Std 802.11-2016, 9.6.8.1, Table 9-307) */ #define WLAN_PA_20_40_BSS_COEX 0 +#define WLAN_PA_DSE_ENABLEMENT 1 +#define WLAN_PA_DSE_DEENABLEMENT 2 +#define WLAN_PA_DSE_REG_LOCATION_ANNOUNCE 3 +#define WLAN_PA_EXT_CHANNEL_SWITCH_ANNOUNCE 4 +#define WLAN_PA_DSE_MEASUREMENT_REQ 5 +#define WLAN_PA_DSE_MEASUREMENT_RESP 6 +#define WLAN_PA_MEASUREMENT_PILOT 7 +#define WLAN_PA_DSE_POWER_CONSTRAINT 8 #define WLAN_PA_VENDOR_SPECIFIC 9 #define WLAN_PA_GAS_INITIAL_REQ 10 #define WLAN_PA_GAS_INITIAL_RESP 11 #define WLAN_PA_GAS_COMEBACK_REQ 12 #define WLAN_PA_GAS_COMEBACK_RESP 13 #define WLAN_TDLS_DISCOVERY_RESPONSE 14 +#define WLAN_PA_LOCATION_TRACK_NOTIFICATION 15 +#define WLAN_PA_QAB_REQUEST_FRAME 16 +#define WLAN_PA_QAB_RESPONSE_FRAME 17 +#define WLAN_PA_QMF_POLICY 18 +#define WLAN_PA_QMF_POLICY_CHANGE 19 +#define WLAN_PA_QLOAD_REQUEST 20 +#define WLAN_PA_QLOAD_REPORT 21 +#define WLAN_PA_HCCA_TXOP_ADVERTISEMENT 22 +#define WLAN_PA_HCCA_TXOP_RESPONSE 23 +#define WLAN_PA_PUBLIC_KEY 24 +#define WLAN_PA_CHANNEL_AVAILABILITY_QUERY 25 +#define WLAN_PA_CHANNEL_SCHEDULE_MANAGEMENT 26 +#define WLAN_PA_CONTACT_VERIFICATION_SIGNAL 27 +#define WLAN_PA_GDD_ENABLEMENT_REQ 28 +#define WLAN_PA_GDD_ENABLEMENT_RESP 29 +#define WLAN_PA_NETWORK_CHANNEL_CONTROL 30 +#define WLAN_PA_WHITE_SPACE_MAP_ANNOUNCEMENT 31 +#define WLAN_PA_FTM_REQUEST 32 +#define WLAN_PA_FTM 33 +#define WLAN_PA_FILS_DISCOVERY 34 -/* Protected Dual of Public Action frames */ +/* Protected Dual of Public Action frames (IEEE Std 802.11-2016, 9.6.11, + * Table 9-332) */ #define WLAN_PROT_DSE_ENABLEMENT 1 #define WLAN_PROT_DSE_DEENABLEMENT 2 #define WLAN_PROT_EXT_CSA 4 @@ -334,6 +547,21 @@ #define WLAN_PROT_GAS_INITIAL_RESP 11 #define WLAN_PROT_GAS_COMEBACK_REQ 12 #define WLAN_PROT_GAS_COMEBACK_RESP 13 +#define WLAN_PROT_QAB_REQUEST_FRAME 16 +#define WLAN_PROT_QAB_RESPONSE_FRAME 17 +#define WLAN_PROT_QMF_POLICY 18 +#define WLAN_PROT_QMF_POLICY_CHANGE 19 +#define WLAN_PROT_QLOAD_REQUEST 20 +#define WLAN_PROT_QLOAD_REPORT 21 +#define WLAN_PROT_HCCA_TXOP_ADVERTISEMENT 22 +#define WLAN_PROT_HCCA_TXOP_RESPONSE 23 +#define WLAN_PROT_CHANNEL_AVAILABILITY_QUERY 25 +#define WLAN_PROT_CHANNEL_SCHEDULE_MANAGEMENT 26 +#define WLAN_PROT_CONTACT_VERIFICATION_SIGNAL 27 +#define WLAN_PROT_GDD_ENABLEMENT_REQ 28 +#define WLAN_PROT_GDD_ENABLEMENT_RESP 29 +#define WLAN_PROT_NETWORK_CHANNEL_CONTROL 30 +#define WLAN_PROT_WHITE_SPACE_MAP_ANNOUNCEMENT 31 /* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */ #define WLAN_SA_QUERY_REQUEST 0 @@ -362,10 +590,14 @@ #define WLAN_RRM_NEIGHBOR_REPORT_REQUEST 4 #define WLAN_RRM_NEIGHBOR_REPORT_RESPONSE 5 -/* Radio Measurement capabilities (from RRM Capabilities IE) */ +/* Radio Measurement capabilities (from RM Enabled Capabilities element) + * IEEE Std 802.11-2016, 9.4.2.45, Table 9-157 */ /* byte 1 (out of 5) */ #define WLAN_RRM_CAPS_LINK_MEASUREMENT BIT(0) #define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1) +#define WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE BIT(4) +#define WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE BIT(5) +#define WLAN_RRM_CAPS_BEACON_REPORT_TABLE BIT(6) /* byte 2 (out of 5) */ #define WLAN_RRM_CAPS_LCI_MEASUREMENT BIT(4) /* byte 5 (out of 5) */ @@ -398,16 +630,18 @@ #define INTERWORKING_ANT_TEST 6 #define INTERWORKING_ANT_WILDCARD 15 -/* Advertisement Protocol ID definitions (IEEE Std 802.11u-2011) */ +/* Advertisement Protocol ID definitions (IEEE Std 802.11-2016, Table 9-215) */ enum adv_proto_id { ACCESS_NETWORK_QUERY_PROTOCOL = 0, MIH_INFO_SERVICE = 1, MIH_CMD_AND_EVENT_DISCOVERY = 2, EMERGENCY_ALERT_SYSTEM = 3, + REGISTERED_LOCATION_QUERY_PROTO = 4, ADV_PROTO_VENDOR_SPECIFIC = 221 }; -/* Access Network Query Protocol info ID definitions (IEEE Std 802.11u-2011) */ +/* Access Network Query Protocol info ID definitions (IEEE Std 802.11-2016, + * Table 9-271; P802.11ai) */ enum anqp_info_id { ANQP_QUERY_LIST = 256, ANQP_CAPABILITY_LIST = 257, @@ -426,9 +660,14 @@ enum anqp_info_id { ANQP_TDLS_CAPABILITY = 270, ANQP_EMERGENCY_NAI = 271, ANQP_NEIGHBOR_REPORT = 272, + ANQP_QUERY_AP_LIST = 273, + ANQP_AP_LIST_RESPONSE = 274, + ANQP_FILS_REALM_INFO = 275, + ANQP_CAG = 276, ANQP_VENUE_URL = 277, ANQP_ADVICE_OF_CHARGE = 278, ANQP_LOCAL_CONTENT = 279, + ANQP_NETWORK_AUTH_TYPE_TIMESTAMP = 280, ANQP_VENDOR_SPECIFIC = 56797 }; @@ -505,6 +744,11 @@ enum lci_req_subelem { LCI_REQ_SUBELEM_MAX_AGE = 4, }; +#define FILS_NONCE_LEN 16 +#define FILS_SESSION_LEN 8 +#define FILS_CACHE_ID_LEN 2 +#define FILS_MAX_KEY_AUTH_LEN 48 + #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ @@ -678,6 +922,16 @@ struct ieee80211_mgmt { u8 variable[]; } STRUCT_PACKED bss_tm_query; struct { + u8 action; /* 11 */ + u8 dialog_token; + u8 req_info; + } STRUCT_PACKED coloc_intf_req; + struct { + u8 action; /* 12 */ + u8 dialog_token; + u8 variable[]; + } STRUCT_PACKED coloc_intf_report; + struct { u8 action; /* 15 */ u8 variable[]; } STRUCT_PACKED slf_prot_action; @@ -887,6 +1141,7 @@ struct ieee80211_ampe_ie { #define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ((u32) BIT(2)) #define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ ((u32) BIT(3)) #define VHT_CAP_SUPP_CHAN_WIDTH_MASK ((u32) BIT(2) | BIT(3)) +#define VHT_CAP_SUPP_CHAN_WIDTH_MASK_SHIFT 2 #define VHT_CAP_RXLDPC ((u32) BIT(4)) #define VHT_CAP_SHORT_GI_80 ((u32) BIT(5)) #define VHT_CAP_SHORT_GI_160 ((u32) BIT(6)) @@ -953,6 +1208,8 @@ struct ieee80211_ampe_ie { #define OSEN_IE_VENDOR_TYPE 0x506f9a12 #define MBO_IE_VENDOR_TYPE 0x506f9a16 #define MBO_OUI_TYPE 22 +#define OWE_IE_VENDOR_TYPE 0x506f9a1c +#define OWE_OUI_TYPE 28 #define WMM_OUI_TYPE 2 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0 @@ -1072,6 +1329,7 @@ enum wmm_ac { #define HS20_INDICATION_OUI_TYPE 16 #define HS20_ANQP_OUI_TYPE 17 #define HS20_OSEN_OUI_TYPE 18 +#define HS20_ROAMING_CONS_SEL_OUI_TYPE 29 #define HS20_STYPE_QUERY_LIST 1 #define HS20_STYPE_CAPABILITY_LIST 2 #define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3 @@ -1082,21 +1340,27 @@ enum wmm_ac { #define HS20_STYPE_OSU_PROVIDERS_LIST 8 #define HS20_STYPE_ICON_REQUEST 10 #define HS20_STYPE_ICON_BINARY_FILE 11 +#define HS20_STYPE_OPERATOR_ICON_METADATA 12 +#define HS20_STYPE_OSU_PROVIDERS_NAI_LIST 13 #define HS20_DGAF_DISABLED 0x01 #define HS20_PPS_MO_ID_PRESENT 0x02 #define HS20_ANQP_DOMAIN_ID_PRESENT 0x04 +#ifndef HS20_VERSION #define HS20_VERSION 0x10 /* Release 2 */ +#endif /* HS20_VERSION */ /* WNM-Notification WFA vendors specific subtypes */ #define HS20_WNM_SUB_REM_NEEDED 0 #define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1 +#define HS20_WNM_T_C_ACCEPTANCE 2 #define HS20_DEAUTH_REASON_CODE_BSS 0 #define HS20_DEAUTH_REASON_CODE_ESS 1 /* MBO v0.0_r19, 4.2: MBO Attributes */ /* Table 4-5: MBO Attributes */ +/* OCE v0.0.10, Table 4-3: OCE Attributes */ enum mbo_attr_id { MBO_ATTR_ID_AP_CAPA_IND = 1, MBO_ATTR_ID_NON_PREF_CHAN_REPORT = 2, @@ -1106,6 +1370,10 @@ enum mbo_attr_id { MBO_ATTR_ID_TRANSITION_REASON = 6, MBO_ATTR_ID_TRANSITION_REJECT_REASON = 7, MBO_ATTR_ID_ASSOC_RETRY_DELAY = 8, + OCE_ATTR_ID_CAPA_IND = 101, + OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT = 102, + OCE_ATTR_ID_REDUCED_WAN_METRICS = 103, + OCE_ATTR_ID_RNR_COMPLETENESS = 104, }; /* MBO v0.0_r19, 4.2.1: MBO AP Capability Indication Attribute */ @@ -1180,9 +1448,17 @@ enum wfa_wnm_notif_subelem_id { WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA = 3, }; -/* MBO v0.0_r25, 4.3: MBO ANQP-elements */ +/* MBO v0.0_r27, 4.3: MBO ANQP-elements */ #define MBO_ANQP_OUI_TYPE 0x12 -#define MBO_ANQP_SUBTYPE_CELL_CONN_PREF 1 +#define MBO_ANQP_SUBTYPE_QUERY_LIST 1 +#define MBO_ANQP_SUBTYPE_CELL_CONN_PREF 2 +#define MAX_MBO_ANQP_SUBTYPE MBO_ANQP_SUBTYPE_CELL_CONN_PREF + +/* OCE v0.0.10, 4.2.1: OCE Capability Indication Attribute */ +#define OCE_RELEASE 1 +#define OCE_RELEASE_MASK (BIT(0) | BIT(1) | BIT(2)) +#define OCE_IS_STA_CFON BIT(3) +#define OCE_IS_NON_OCE_AP_PRESENT BIT(4) /* Wi-Fi Direct (P2P) */ @@ -1331,7 +1607,9 @@ enum wifi_display_subelem { WFD_SUBELEM_COUPLED_SINK = 6, WFD_SUBELEM_EXT_CAPAB = 7, WFD_SUBELEM_LOCAL_IP_ADDRESS = 8, - WFD_SUBELEM_SESSION_INFO = 9 + WFD_SUBELEM_SESSION_INFO = 9, + WFD_SUBELEM_MAC_INFO = 10, + WFD_SUBELEM_R2_DEVICE_INFO = 11, }; /* 802.11s */ @@ -1363,41 +1641,6 @@ enum plink_action_field { #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */ -/* cipher suite selectors */ -#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00 -#define WLAN_CIPHER_SUITE_WEP40 0x000FAC01 -#define WLAN_CIPHER_SUITE_TKIP 0x000FAC02 -/* reserved: 0x000FAC03 */ -#define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 -#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 -#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 -#define WLAN_CIPHER_SUITE_NO_GROUP_ADDR 0x000FAC07 -#define WLAN_CIPHER_SUITE_GCMP 0x000FAC08 -#define WLAN_CIPHER_SUITE_GCMP_256 0x000FAC09 -#define WLAN_CIPHER_SUITE_CCMP_256 0x000FAC0A -#define WLAN_CIPHER_SUITE_BIP_GMAC_128 0x000FAC0B -#define WLAN_CIPHER_SUITE_BIP_GMAC_256 0x000FAC0C -#define WLAN_CIPHER_SUITE_BIP_CMAC_256 0x000FAC0D - -#define WLAN_CIPHER_SUITE_SMS4 0x00147201 - -#define WLAN_CIPHER_SUITE_CKIP 0x00409600 -#define WLAN_CIPHER_SUITE_CKIP_CMIC 0x00409601 -#define WLAN_CIPHER_SUITE_CMIC 0x00409602 -#define WLAN_CIPHER_SUITE_KRK 0x004096FF /* for nl80211 use only */ - -/* AKM suite selectors */ -#define WLAN_AKM_SUITE_8021X 0x000FAC01 -#define WLAN_AKM_SUITE_PSK 0x000FAC02 -#define WLAN_AKM_SUITE_FT_8021X 0x000FAC03 -#define WLAN_AKM_SUITE_FT_PSK 0x000FAC04 -#define WLAN_AKM_SUITE_8021X_SHA256 0x000FAC05 -#define WLAN_AKM_SUITE_PSK_SHA256 0x000FAC06 -#define WLAN_AKM_SUITE_8021X_SUITE_B 0x000FAC11 -#define WLAN_AKM_SUITE_8021X_SUITE_B_192 0x000FAC12 -#define WLAN_AKM_SUITE_CCKM 0x00409600 -#define WLAN_AKM_SUITE_OSEN 0x506f9a01 - /* IEEE 802.11v - WNM Action field values */ enum wnm_action { @@ -1559,6 +1802,102 @@ struct rrm_link_measurement_report { u8 variable[0]; } STRUCT_PACKED; +/* IEEE Std 802.11-2016, 9.4.2.21 - Measurement Request element */ +struct rrm_measurement_request_element { + u8 eid; /* Element ID */ + u8 len; /* Length */ + u8 token; /* Measurement Token */ + u8 mode; /* Measurement Request Mode */ + u8 type; /* Measurement Type */ + u8 variable[0]; /* Measurement Request */ +} STRUCT_PACKED; + +/* IEEE Std 802.11-2016, Figure 9-148 - Measurement Request Mode field */ +#define MEASUREMENT_REQUEST_MODE_PARALLEL BIT(0) +#define MEASUREMENT_REQUEST_MODE_ENABLE BIT(1) +#define MEASUREMENT_REQUEST_MODE_REQUEST BIT(2) +#define MEASUREMENT_REQUEST_MODE_REPORT BIT(3) +#define MEASUREMENT_REQUEST_MODE_DURATION_MANDATORY BIT(4) + +/* IEEE Std 802.11-2016, 9.4.2.21.7 - Beacon request */ +struct rrm_measurement_beacon_request { + u8 oper_class; /* Operating Class */ + u8 channel; /* Channel Number */ + le16 rand_interval; /* Randomization Interval (in TUs) */ + le16 duration; /* Measurement Duration (in TUs) */ + u8 mode; /* Measurement Mode */ + u8 bssid[ETH_ALEN]; /* BSSID */ + u8 variable[0]; /* Optional Subelements */ +} STRUCT_PACKED; + +/* + * IEEE Std 802.11-2016, Table 9-87 - Measurement Mode definitions for Beacon + * request + */ +enum beacon_report_mode { + BEACON_REPORT_MODE_PASSIVE = 0, + BEACON_REPORT_MODE_ACTIVE = 1, + BEACON_REPORT_MODE_TABLE = 2, +}; + +/* IEEE Std 802.11-2016, Table 9-88 - Beacon Request subelement IDs */ +#define WLAN_BEACON_REQUEST_SUBELEM_SSID 0 +#define WLAN_BEACON_REQUEST_SUBELEM_INFO 1 /* Beacon Reporting */ +#define WLAN_BEACON_REQUEST_SUBELEM_DETAIL 2 /* Reporting Detail */ +#define WLAN_BEACON_REQUEST_SUBELEM_REQUEST 10 +#define WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL 51 /* AP Channel Report */ +#define WLAN_BEACON_REQUEST_SUBELEM_VENDOR 221 + +/* + * IEEE Std 802.11-2016, Table 9-90 - Reporting Detail values + */ +enum beacon_report_detail { + /* No fixed-length fields or elements */ + BEACON_REPORT_DETAIL_NONE = 0, + /* All fixed-length fields and any requested elements in the Request + * element if present */ + BEACON_REPORT_DETAIL_REQUESTED_ONLY = 1, + /* All fixed-length fields and elements (default, used when Reporting + * Detail subelement is not included in a Beacon request) */ + BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS = 2, +}; + +/* IEEE Std 802.11-2016, 9.4.2.22 - Measurement Report element */ +struct rrm_measurement_report_element { + u8 eid; /* Element ID */ + u8 len; /* Length */ + u8 token; /* Measurement Token */ + u8 mode; /* Measurement Report Mode */ + u8 type; /* Measurement Type */ + u8 variable[0]; /* Measurement Report */ +} STRUCT_PACKED; + +/* IEEE Std 802.11-2016, Figure 9-192 - Measurement Report Mode field */ +#define MEASUREMENT_REPORT_MODE_ACCEPT 0 +#define MEASUREMENT_REPORT_MODE_REJECT_LATE BIT(0) +#define MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE BIT(1) +#define MEASUREMENT_REPORT_MODE_REJECT_REFUSED BIT(2) + +/* IEEE Std 802.11-2016, 9.4.2.22.7 - Beacon report */ +struct rrm_measurement_beacon_report { + u8 op_class; /* Operating Class */ + u8 channel; /* Channel Number */ + le64 start_time; /* Actual Measurement Start Time + * (in TSF of the BSS requesting the measurement) */ + le16 duration; /* in TUs */ + u8 report_info; /* Reported Frame Information */ + u8 rcpi; /* RCPI */ + u8 rsni; /* RSNI */ + u8 bssid[ETH_ALEN]; /* BSSID */ + u8 antenna_id; /* Antenna ID */ + le32 parent_tsf; /* Parent TSF */ + u8 variable[0]; /* Optional Subelements */ +} STRUCT_PACKED; + +/* IEEE Std 802.11-2016, Table 9-112 - Beacon report Subelement IDs */ +#define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY 1 +#define WLAN_BEACON_REPORT_SUBELEM_VENDOR 221 + /* IEEE Std 802.11ad-2012 - Multi-band element */ struct multi_band_ie { u8 eid; /* WLAN_EID_MULTI_BAND */ @@ -1660,4 +1999,55 @@ enum nr_chan_width { NR_CHAN_WIDTH_80P80 = 4, }; +struct ieee80211_he_capabilities { + u8 he_mac_capab_info[5]; + u8 he_phy_capab_info[9]; + u8 he_txrx_mcs_support[12]; /* TODO: 4, 8, or 12 octets */ + /* PPE Thresholds (optional) */ +} STRUCT_PACKED; + +struct ieee80211_he_operation { + u32 he_oper_params; + u8 he_mcs_nss_set[2]; + u8 vht_op_info_chwidth; + u8 vht_op_info_chan_center_freq_seg0_idx; + u8 vht_op_info_chan_center_freq_seg1_idx; + /* Followed by conditional MaxBSSID Indicator subfield (u8) */ +} STRUCT_PACKED; + +/* HE Capabilities Information defines */ +#define HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX 3 +#define HE_PHYCAP_SU_BEAMFORMER_CAPAB ((u8) BIT(7)) +#define HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX 4 +#define HE_PHYCAP_SU_BEAMFORMEE_CAPAB ((u8) BIT(0)) +#define HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX 4 +#define HE_PHYCAP_MU_BEAMFORMER_CAPAB ((u8) BIT(1)) + +/* HE Operation defines */ +#define HE_OPERATION_BSS_COLOR_MASK ((u32) (BIT(0) | BIT(1) | \ + BIT(2) | BIT(3) | \ + BIT(4) | BIT(5))) +#define HE_OPERATION_DFLT_PE_DURATION_MASK ((u32) (BIT(6) | BIT(7) | \ + BIT(8))) +#define HE_OPERATION_DFLT_PE_DURATION_OFFSET 6 +#define HE_OPERATION_TWT_REQUIRED ((u32) BIT(9)) +#define HE_OPERATION_RTS_THRESHOLD_MASK ((u32) (BIT(10) | BIT(11) | \ + BIT(12) | BIT(13) | \ + BIT(14) | BIT(15) | \ + BIT(16) | BIT(17) | \ + BIT(18) | BIT(19))) +#define HE_OPERATION_RTS_THRESHOLD_OFFSET 10 +#define HE_OPERATION_PARTIAL_BSS_COLOR ((u32) BIT(20)) +#define HE_OPERATION_MAX_BSSID_INDICATOR_MASK ((u32) (BIT(21) | BIT(22) | \ + BIT(23) | BIT(24) | \ + BIT(25) | BIT(26) | \ + BIT(27) | BIT(28))) +#define HE_OPERATION_MAX_BSSID_INDICATOR_OFFSET 21 +#define HE_OPERATION_TX_BSSID_INDICATOR ((u32) BIT(29)) +#define HE_OPERATION_BSS_COLOR_DISABLED ((u32) BIT(30)) +#define HE_OPERATION_BSS_DUAL_BEACON ((u32) BIT(31)) + +/* DPP Public Action frame identifiers - OUI_WFA */ +#define DPP_OUI_TYPE 0x1A + #endif /* IEEE802_11_DEFS_H */ diff --git a/src/common/ieee802_1x_defs.h b/src/common/ieee802_1x_defs.h index a0c1d1bfafc4..e7acff108eb3 100644 --- a/src/common/ieee802_1x_defs.h +++ b/src/common/ieee802_1x_defs.h @@ -12,6 +12,8 @@ #define CS_ID_LEN 8 #define CS_ID_GCM_AES_128 0x0080020001000001ULL #define CS_NAME_GCM_AES_128 "GCM-AES-128" +#define CS_ID_GCM_AES_256 0x0080c20001000002ULL +#define CS_NAME_GCM_AES_256 "GCM-AES-256" enum macsec_policy { /** @@ -25,6 +27,12 @@ enum macsec_policy { * Disabled MACsec - do not secure sessions. */ DO_NOT_SECURE, + + /** + * Should secure sessions, and try to use encryption. + * Like @SHOULD_SECURE, this follows the key server's decision. + */ + SHOULD_ENCRYPT, }; diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h index 8dff30382b60..b85c6c347a79 100644 --- a/src/common/privsep_commands.h +++ b/src/common/privsep_commands.h @@ -9,6 +9,7 @@ #ifndef PRIVSEP_COMMANDS_H #define PRIVSEP_COMMANDS_H +#include "drivers/driver.h" #include "common/ieee802_11_defs.h" enum privsep_cmd { @@ -29,8 +30,17 @@ enum privsep_cmd { PRIVSEP_CMD_AUTHENTICATE, }; -struct privsep_cmd_authenticate -{ +#define PRIVSEP_MAX_SCAN_FREQS 50 + +struct privsep_cmd_scan { + unsigned int num_ssids; + u8 ssids[WPAS_MAX_SCAN_SSIDS][32]; + u8 ssid_lens[WPAS_MAX_SCAN_SSIDS]; + unsigned int num_freqs; + u16 freqs[PRIVSEP_MAX_SCAN_FREQS]; +}; + +struct privsep_cmd_authenticate { int freq; u8 bssid[ETH_ALEN]; u8 ssid[SSID_MAX_LEN]; @@ -42,13 +52,12 @@ struct privsep_cmd_authenticate int wep_tx_keyidx; int local_state_change; int p2p; - size_t sae_data_len; + size_t auth_data_len; /* followed by ie_len bytes of ie */ - /* followed by sae_data_len bytes of sae_data */ + /* followed by auth_data_len bytes of auth_data */ }; -struct privsep_cmd_associate -{ +struct privsep_cmd_associate { u8 bssid[ETH_ALEN]; u8 ssid[SSID_MAX_LEN]; size_t ssid_len; @@ -64,8 +73,7 @@ struct privsep_cmd_associate /* followed by wpa_ie_len bytes of wpa_ie */ }; -struct privsep_cmd_set_key -{ +struct privsep_cmd_set_key { int alg; u8 addr[ETH_ALEN]; int key_idx; @@ -84,7 +92,6 @@ enum privsep_event { PRIVSEP_EVENT_MICHAEL_MIC_FAILURE, PRIVSEP_EVENT_INTERFACE_STATUS, PRIVSEP_EVENT_PMKID_CANDIDATE, - PRIVSEP_EVENT_STKSTART, PRIVSEP_EVENT_FT_RESPONSE, PRIVSEP_EVENT_RX_EAPOL, PRIVSEP_EVENT_SCAN_STARTED, diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h index adaec890b58d..7c75d0804553 100644 --- a/src/common/qca-vendor.h +++ b/src/common/qca-vendor.h @@ -1,6 +1,7 @@ /* * Qualcomm Atheros OUI and vendor specific assignments - * Copyright (c) 2014-2015, Qualcomm Atheros, Inc. + * Copyright (c) 2014-2017, Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -49,7 +50,10 @@ enum qca_radiotap_vendor_ids { * * @QCA_NL80211_VENDOR_SUBCMD_NAN: NAN command/event which is used to pass * NAN Request/Response and NAN Indication messages. These messages are - * interpreted between the framework and the firmware component. + * interpreted between the framework and the firmware component. While + * sending the command from userspace to the driver, payload is not + * encapsulated inside any attribute. Attribute QCA_WLAN_VENDOR_ATTR_NAN + * is used when receiving vendor events in userspace from the driver. * * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY: Set key operation that can be * used to configure PMK to the driver even when not connected. This can @@ -90,6 +94,75 @@ enum qca_radiotap_vendor_ids { * which supports DFS offloading, to indicate a radar pattern has been * detected. The channel is now unusable. * + * @QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET: Get the feature bitmap + * based on enum wifi_logger_supported_features. Attributes defined in + * enum qca_wlan_vendor_attr_get_logger_features. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA: Get the ring data from a particular + * logger ring, QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID is passed as the + * attribute for this command. Attributes defined in + * enum qca_wlan_vendor_attr_wifi_logger_start. + * + * @QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES: Get the supported TDLS + * capabilities of the driver, parameters includes the attributes defined + * in enum qca_wlan_vendor_attr_tdls_get_capabilities. + * + * @QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS: Vendor command used to offload + * sending of certain periodic IP packet to firmware, attributes defined in + * enum qca_wlan_vendor_attr_offloaded_packets. + * + * @QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI: Command used to configure RSSI + * monitoring, defines min and max RSSI which are configured for RSSI + * monitoring. Also used to notify the RSSI breach and provides the BSSID + * and RSSI value that was breached. Attributes defined in + * enum qca_wlan_vendor_attr_rssi_monitoring. + * + * @QCA_NL80211_VENDOR_SUBCMD_NDP: Command used for performing various NAN + * Data Path (NDP) related operations, attributes defined in + * enum qca_wlan_vendor_attr_ndp_params. + * + * @QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD: Command used to enable/disable + * Neighbour Discovery offload, attributes defined in + * enum qca_wlan_vendor_attr_nd_offload. + * + * @QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER: Used to set/get the various + * configuration parameter for BPF packet filter, attributes defined in + * enum qca_wlan_vendor_attr_packet_filter. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE: Gets the driver-firmware + * maximum supported size, attributes defined in + * enum qca_wlan_vendor_drv_info. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS: Command to get various + * data about wake reasons and datapath IP statistics, attributes defined + * in enum qca_wlan_vendor_attr_wake_stats. + * + * @QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG: Command used to set configuration + * for IEEE 802.11 communicating outside the context of a basic service + * set, called OCB command. Uses the attributes defines in + * enum qca_wlan_vendor_attr_ocb_set_config. + * + * @QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME: Command used to set OCB + * UTC time. Use the attributes defines in + * enum qca_wlan_vendor_attr_ocb_set_utc_time. + * + * @QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT: Command used to start + * sending OCB timing advert frames. Uses the attributes defines in + * enum qca_wlan_vendor_attr_ocb_start_timing_advert. + * + * @QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT: Command used to stop + * OCB timing advert. Uses the attributes defines in + * enum qca_wlan_vendor_attr_ocb_stop_timing_advert. + * + * @QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER: Command used to get TSF + * timer value. Uses the attributes defines in + * enum qca_wlan_vendor_attr_ocb_get_tsf_resp. + * + * @QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES: Command/event to update the + * link properties of the respective interface. As an event, is used + * to notify the connected station's status. The attributes for this + * command are defined in enum qca_wlan_vendor_attr_link_properties. + * * @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START: Command used to * start the P2P Listen offload function in device and pass the listen * channel, period, interval, count, device types, and vendor specific @@ -164,8 +237,11 @@ enum qca_radiotap_vendor_ids { * * @QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS: Perform a standalone AOA (angle of * arrival) measurement with a single peer. Specify peer MAC address in - * QCA_WLAN_VENDOR_ATTR_MAC_ADDR and measurement type in - * QCA_WLAN_VENDOR_ATTR_AOA_TYPE. Measurement result is reported in + * QCA_WLAN_VENDOR_ATTR_MAC_ADDR and optionally frequency (MHz) in + * QCA_WLAN_VENDOR_ATTR_FREQ (if not specified, locate peer in kernel + * scan results cache and use the frequency from there). + * Also specify measurement type in QCA_WLAN_VENDOR_ATTR_AOA_TYPE. + * Measurement result is reported in * QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT event. * * @QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS: Abort an AOA measurement. Specify @@ -185,6 +261,244 @@ enum qca_radiotap_vendor_ids { * * @QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI: Get antenna RSSI value for a * specific chain. + * + * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG: Get low level + * configuration for a DMG RF sector. Specify sector index in + * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX, sector type in + * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and RF modules + * to return sector information for in + * QCA_WLAN_VENDOR_ATTR_DMG_RF_MODULE_MASK. Returns sector configuration + * in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG. Also return the + * exact time where information was captured in + * QCA_WLAN_VENDOR_ATTR_TSF. + * + * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG: Set low level + * configuration for a DMG RF sector. Specify sector index in + * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX, sector type in + * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and sector configuration + * for one or more DMG RF modules in + * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG. + * + * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR: Get selected + * DMG RF sector for a station. This is the sector that the HW + * will use to communicate with the station. Specify the MAC address + * of associated station/AP/PCP in QCA_WLAN_VENDOR_ATTR_MAC_ADDR (not + * needed for unassociated station). Specify sector type to return in + * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE. Returns the selected + * sector index in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX. + * Also return the exact time where the information was captured + * in QCA_WLAN_VENDOR_ATTR_TSF. + * + * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR: Set the + * selected DMG RF sector for a station. This is the sector that + * the HW will use to communicate with the station. + * Specify the MAC address of associated station/AP/PCP in + * QCA_WLAN_VENDOR_ATTR_MAC_ADDR, the sector type to select in + * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and the sector index + * in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX. + * The selected sector will be locked such that it will not be + * modified like it normally does (for example when station + * moves around). To unlock the selected sector for a station + * pass the special value 0xFFFF in the sector index. To unlock + * all connected stations also pass a broadcast MAC address. + * + * @QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS: Configure the TDLS behavior + * in the host driver. The different TDLS configurations are defined + * by the attributes in enum qca_wlan_vendor_attr_tdls_configuration. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES: Query device IEEE 802.11ax HE + * capabilities. The response uses the attributes defined in + * enum qca_wlan_vendor_attr_get_he_capabilities. + * + * @QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN: Abort an ongoing vendor scan that was + * started with QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN. This command + * carries the scan cookie of the corresponding scan request. The scan + * cookie is represented by QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE. + * + * @QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS: Set the Specific + * Absorption Rate (SAR) power limits. A critical regulation for + * FCC compliance, OEMs require methods to set SAR limits on TX + * power of WLAN/WWAN. enum qca_vendor_attr_sar_limits + * attributes are used with this command. + * + * @QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS: This command/event is used by the + * host driver for offloading the implementation of Auto Channel Selection + * (ACS) to an external user space entity. This interface is used as the + * event from the host driver to the user space entity and also as the + * request from the user space entity to the host driver. The event from + * the host driver is used by the user space entity as an indication to + * start the ACS functionality. The attributes used by this event are + * represented by the enum qca_wlan_vendor_attr_external_acs_event. + * User space entity uses the same interface to inform the host driver with + * selected channels after the ACS operation using the attributes defined + * by enum qca_wlan_vendor_attr_external_acs_channels. + * + * @QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE: Vendor event carrying the + * requisite information leading to a power save failure. The information + * carried as part of this event is represented by the + * enum qca_attr_chip_power_save_failure attributes. + * + * @QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET: Start/Stop the NUD statistics + * collection. Uses attributes defined in enum qca_attr_nud_stats_set. + * + * @QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET: Get the NUD statistics. These + * statistics are represented by the enum qca_attr_nud_stats_get + * attributes. + * + * @QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS: Sub-command to fetch + * the BSS transition status, whether accept or reject, for a list of + * candidate BSSIDs provided by the userspace. This uses the vendor + * attributes QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON and + * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO. The userspace shall specify + * the attributes QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON and an + * array of QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID nested in + * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO in the request. In the response + * the driver shall specify array of + * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID and + * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS pairs nested in + * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO. + * + * @QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL: Set the trace level for a + * specific QCA module. The trace levels are represented by + * enum qca_attr_trace_level attributes. + * + * @QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT: Set the Beam Refinement + * Protocol antenna limit in different modes. See enum + * qca_wlan_vendor_attr_brp_ant_limit_mode. + * + * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START: Start spectral scan. The scan + * parameters are specified by enum qca_wlan_vendor_attr_spectral_scan. + * This returns a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE) + * identifying the operation in success case. + * + * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP: Stop spectral scan. This uses + * a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE) from + * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START to identify the scan to + * be stopped. + * + * @QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS: Set the active Type Of Service on the + * specific interface. This can be used to modify some of the low level + * scan parameters (off channel dwell time, home channel time) in the + * driver/firmware. These parameters are maintained within the host driver. + * This command is valid only when the interface is in the connected state. + * These scan parameters shall be reset by the driver/firmware once + * disconnected. The attributes used with this command are defined in + * enum qca_wlan_vendor_attr_active_tos. + * + * @QCA_NL80211_VENDOR_SUBCMD_HANG: Event indicating to the user space that the + * driver has detected an internal failure. This event carries the + * information indicating the reason that triggered this detection. The + * attributes for this command are defined in + * enum qca_wlan_vendor_attr_hang. + * + * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CONFIG: Get the current values + * of spectral parameters used. The spectral scan parameters are specified + * by enum qca_wlan_vendor_attr_spectral_scan. + * + * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS: Get the debug stats + * for spectral scan functionality. The debug stats are specified by + * enum qca_wlan_vendor_attr_spectral_diag_stats. + * + * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO: Get spectral + * scan system capabilities. The capabilities are specified + * by enum qca_wlan_vendor_attr_spectral_cap. + * + * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS: Get the current + * status of spectral scan. The status values are specified + * by enum qca_wlan_vendor_attr_spectral_scan_status. + * + * @QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING: Sub-command to flush + * peer pending packets. Specify the peer MAC address in + * QCA_WLAN_VENDOR_ATTR_PEER_ADDR and the access category of the packets + * in QCA_WLAN_VENDOR_ATTR_AC. The attributes are listed + * in enum qca_wlan_vendor_attr_flush_pending. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO: Get vendor specific Representative + * RF Operating Parameter (RROP) information. The attributes for this + * information are defined in enum qca_wlan_vendor_attr_rrop_info. This is + * intended for use by external Auto Channel Selection applications. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS: Get the Specific Absorption Rate + * (SAR) power limits. This is a companion to the command + * @QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS and is used to retrieve the + * settings currently in use. The attributes returned by this command are + * defined by enum qca_vendor_attr_sar_limits. + * + * @QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO: Provides the current behavior of + * the WLAN hardware MAC. Also, provides the WLAN netdev interface + * information attached to the respective MAC. + * This works both as a query (user space asks the current mode) or event + * interface (driver advertising the current mode to the user space). + * Driver does not trigger this event for temporary hardware mode changes. + * Mode changes w.r.t Wi-Fi connection update (VIZ creation / deletion, + * channel change, etc.) are updated with this event. Attributes for this + * interface are defined in enum qca_wlan_vendor_attr_mac. + * + * @QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH: Set MSDU queue depth threshold + * per peer per TID. Attributes for this command are define in + * enum qca_wlan_set_qdepth_thresh_attr. + * @QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD: Provides the thermal shutdown action + * guide for WLAN driver. Request to suspend of driver and FW if the + * temperature is higher than the suspend threshold; resume action is + * requested to driver if the temperature is lower than the resume + * threshold. In user poll mode, request temperature data by user. For test + * purpose, getting thermal shutdown configuration parameters is needed. + * Attributes for this interface are defined in + * enum qca_wlan_vendor_attr_thermal_cmd. + * @QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT: Thermal events reported from + * driver. Thermal temperature and indication of resume completion are + * reported as thermal events. The attributes for this command are defined + * in enum qca_wlan_vendor_attr_thermal_event. + * + * @QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION: Sub command to set WiFi + * test configuration. Attributes for this command are defined in + * enum qca_wlan_vendor_attr_wifi_test_config. + * + * @QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER: This command is used to configure an + * RX filter to receive frames from stations that are active on the + * operating channel, but not associated with the local device (e.g., STAs + * associated with other APs). Filtering is done based on a list of BSSIDs + * and STA MAC addresses added by the user. This command is also used to + * fetch the statistics of unassociated stations. The attributes used with + * this command are defined in enum qca_wlan_vendor_attr_bss_filter. + * + * @QCA_NL80211_VENDOR_SUBCMD_NAN_EXT: An extendable version of NAN vendor + * command. The earlier command for NAN, QCA_NL80211_VENDOR_SUBCMD_NAN, + * carried a payload which was a binary blob of data. The command was not + * extendable to send more information. The newer version carries the + * legacy blob encapsulated within an attribute and can be extended with + * additional vendor attributes that can enhance the NAN command interface. + * @QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT: Event to indicate scan triggered + * or stopped within driver/firmware in order to initiate roaming. The + * attributes used with this event are defined in enum + * qca_wlan_vendor_attr_roam_scan. Some drivers may not send these events + * in few cases, e.g., if the host processor is sleeping when this event + * is generated in firmware. + * + * @QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG: This command is used to + * configure parameters per peer to capture Channel Frequency Response + * (CFR) and enable Periodic CFR capture. The attributes for this command + * are defined in enum qca_wlan_vendor_peer_cfr_capture_attr. + * + * @QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT: Event to indicate changes + * in throughput dynamically. The driver estimates the throughput based on + * number of packets being transmitted/received per second and indicates + * the changes in throughput to user space. Userspace tools can use this + * information to configure kernel's TCP parameters in order to achieve + * peak throughput. Optionally, the driver will also send guidance on + * modifications to kernel's TCP parameters which can be referred by + * userspace tools. The attributes used with this event are defined in enum + * qca_wlan_vendor_attr_throughput_change. + * + * @QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG: This command is used to set + * priorities among different types of traffic during coex scenarios. + * Current supported prioritization is among WLAN/BT/ZIGBEE with different + * profiles mentioned in enum qca_coex_config_profiles. The associated + * attributes used with this command are defined in enum + * qca_vendor_attr_coex_config. + * + * Based on the config provided, FW will boost the weight and prioritize + * the traffic for that subsystem (WLAN/BT/Zigbee). */ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, @@ -194,7 +508,7 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10, QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY = 11, QCA_NL80211_VENDOR_SUBCMD_NAN = 12, - QCA_NL80211_VENDOR_SUBMCD_STATS_EXT = 13, + QCA_NL80211_VENDOR_SUBCMD_STATS_EXT = 13, QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET = 14, QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET = 15, QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR = 16, @@ -236,11 +550,33 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED = 58, QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED = 59, QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED = 60, - /* 61-73 - reserved for QCA */ + QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO = 61, + QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START = 62, + QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP = 63, + QCA_NL80211_VENDOR_SUBCMD_ROAM = 64, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SSID_HOTLIST = 65, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_SSID_HOTLIST = 66, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_SSID_FOUND = 67, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_SSID_LOST = 68, + QCA_NL80211_VENDOR_SUBCMD_PNO_SET_LIST = 69, + QCA_NL80211_VENDOR_SUBCMD_PNO_SET_PASSPOINT_LIST = 70, + QCA_NL80211_VENDOR_SUBCMD_PNO_RESET_PASSPOINT_LIST = 71, + QCA_NL80211_VENDOR_SUBCMD_PNO_NETWORK_FOUND = 72, + QCA_NL80211_VENDOR_SUBCMD_PNO_PASSPOINT_NETWORK_FOUND = 73, /* Wi-Fi configuration subcommands */ QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION = 74, QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION = 75, - /* 76-90 - reserved for QCA */ + QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET = 76, + QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA = 77, + QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES = 78, + QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS = 79, + QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI = 80, + QCA_NL80211_VENDOR_SUBCMD_NDP = 81, + QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD = 82, + QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER = 83, + QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE = 84, + QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS = 85, + /* 86-90 - reserved for QCA */ QCA_NL80211_VENDOR_SUBCMD_DATA_OFFLOAD = 91, QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG = 92, QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME = 93, @@ -285,21 +621,66 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT = 136, QCA_NL80211_VENDOR_SUBCMD_ENCRYPTION_TEST = 137, QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI = 138, + /* DMG low level RF sector operations */ + QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG = 139, + QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG = 140, + QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR = 141, + QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR = 142, + QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS = 143, + QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES = 144, + QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN = 145, + QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS = 146, + QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS = 147, + QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE = 148, + QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET = 149, + QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET = 150, + QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS = 151, + QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL = 152, + QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT = 153, + QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START = 154, + QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP = 155, + QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS = 156, + QCA_NL80211_VENDOR_SUBCMD_HANG = 157, + QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CONFIG = 158, + QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS = 159, + QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO = 160, + QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS = 161, + /* Flush peer pending data */ + QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING = 162, + QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO = 163, + QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS = 164, + QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO = 165, + QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH = 166, + /* Thermal shutdown commands to protect wifi chip */ + QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD = 167, + QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT = 168, + /* Wi-Fi test configuration subcommand */ + QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION = 169, + /* Frame filter operations for other BSSs/unassociated STAs */ + QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER = 170, + QCA_NL80211_VENDOR_SUBCMD_NAN_EXT = 171, + QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT = 172, + QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG = 173, + QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT = 174, + QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG = 175, }; - enum qca_wlan_vendor_attr { QCA_WLAN_VENDOR_ATTR_INVALID = 0, /* used by QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY */ QCA_WLAN_VENDOR_ATTR_DFS = 1, - /* used by QCA_NL80211_VENDOR_SUBCMD_NAN */ + /* Used only when driver sends vendor events to the userspace under the + * command QCA_NL80211_VENDOR_SUBCMD_NAN. Not used when userspace sends + * commands to the driver. + */ QCA_WLAN_VENDOR_ATTR_NAN = 2, /* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */ QCA_WLAN_VENDOR_ATTR_STATS_EXT = 3, /* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */ QCA_WLAN_VENDOR_ATTR_IFINDEX = 4, /* used by QCA_NL80211_VENDOR_SUBCMD_ROAMING, u32 with values defined - * by enum qca_roaming_policy. */ + * by enum qca_roaming_policy. + */ QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY = 5, QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6, /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */ @@ -387,17 +768,87 @@ enum qca_wlan_vendor_attr { QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT = 25, /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command * to specify the chain number (unsigned 32 bit value) to inquire - * the corresponding antenna RSSI value */ + * the corresponding antenna RSSI value + */ QCA_WLAN_VENDOR_ATTR_CHAIN_INDEX = 26, /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command - * to report the specific antenna RSSI value (unsigned 32 bit value) */ + * to report the specific antenna RSSI value (unsigned 32 bit value) + */ QCA_WLAN_VENDOR_ATTR_CHAIN_RSSI = 27, + /* Frequency in MHz, various uses. Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_FREQ = 28, + /* TSF timer value, unsigned 64 bit value. + * May be returned by various commands. + */ + QCA_WLAN_VENDOR_ATTR_TSF = 29, + /* DMG RF sector index, unsigned 16 bit number. Valid values are + * 0..127 for sector indices or 65535 as special value used to + * unlock sector selection in + * QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR. + */ + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX = 30, + /* DMG RF sector type, unsigned 8 bit value. One of the values + * in enum qca_wlan_vendor_attr_dmg_rf_sector_type. + */ + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE = 31, + /* Bitmask of DMG RF modules for which information is requested. Each + * bit corresponds to an RF module with the same index as the bit + * number. Unsigned 32 bit number but only low 8 bits can be set since + * all DMG chips currently have up to 8 RF modules. + */ + QCA_WLAN_VENDOR_ATTR_DMG_RF_MODULE_MASK = 32, + /* Array of nested attributes where each entry is DMG RF sector + * configuration for a single RF module. + * Attributes for each entry are taken from enum + * qca_wlan_vendor_attr_dmg_rf_sector_cfg. + * Specified in QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG + * and returned by QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG. + */ + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG = 33, + /* Used in QCA_NL80211_VENDOR_SUBCMD_STATS_EXT command + * to report frame aggregation statistics to userspace. + */ + QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_NUM = 34, + QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_INFO = 35, + /* Unsigned 8-bit value representing MBO transition reason code as + * provided by the AP used by subcommand + * QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS. This is + * specified by the userspace in the request to the driver. + */ + QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON = 36, + /* Array of nested attributes, BSSID and status code, used by subcommand + * QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS, where each + * entry is taken from enum qca_wlan_vendor_attr_btm_candidate_info. + * The userspace space specifies the list/array of candidate BSSIDs in + * the order of preference in the request. The driver specifies the + * status code, for each BSSID in the list, in the response. The + * acceptable candidates are listed in the order preferred by the + * driver. + */ + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO = 37, + /* Used in QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT command + * See enum qca_wlan_vendor_attr_brp_ant_limit_mode. + */ + QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE = 38, + /* Used in QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT command + * to define the number of antennas to use for BRP. + * different purpose in each ANT_LIMIT_MODE: + * DISABLE - ignored + * EFFECTIVE - upper limit to number of antennas to be used + * FORCE - exact number of antennas to be used + * unsigned 8 bit value + */ + QCA_WLAN_VENDOR_ATTR_BRP_ANT_NUM_LIMIT = 39, + /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command + * to report the corresponding antenna index to the chain RSSI value + */ + QCA_WLAN_VENDOR_ATTR_ANTENNA_INFO = 40, + /* keep last */ QCA_WLAN_VENDOR_ATTR_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1, }; - enum qca_roaming_policy { QCA_ROAMING_NOT_ALLOWED, QCA_ROAMING_ALLOWED_WITHIN_ESS, @@ -413,6 +864,38 @@ enum qca_wlan_vendor_attr_roam_auth { QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS, + /* Indicates the status of re-association requested by user space for + * the BSSID specified by QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID. + * Type u16. + * Represents the status code from AP. Use + * %WLAN_STATUS_UNSPECIFIED_FAILURE if the device cannot give you the + * real status code for failures. + */ + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_STATUS, + /* This attribute indicates that the old association was maintained when + * a re-association is requested by user space and that re-association + * attempt fails (i.e., cannot connect to the requested BSS, but can + * remain associated with the BSS with which the association was in + * place when being requested to roam). Used along with + * WLAN_VENDOR_ATTR_ROAM_AUTH_STATUS to indicate the current + * re-association status. Type flag. + * This attribute is applicable only for re-association failure cases. + */ + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RETAIN_CONNECTION, + /* This attribute specifies the PMK if one was newly generated during + * FILS roaming. This is added to the PMKSA cache and is used in + * subsequent connections with PMKSA caching. + */ + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK = 11, + /* This attribute specifies the PMKID used/generated for the current + * FILS roam. This is used in subsequent connections with PMKSA caching. + */ + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID = 12, + /* A 16-bit unsigned value specifying the next sequence number to use + * in ERP message in the currently associated realm. This is used in + * doing subsequent ERP based connections in the same realm. + */ + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM = 13, /* keep last */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX = @@ -493,13 +976,24 @@ enum qca_wlan_vendor_acs_hw_mode { * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic * band selection based on channel selection results. * @QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS: Device supports - * simultaneous off-channel operations. + * simultaneous off-channel operations. * @QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD: Device supports P2P * Listen offload; a mechanism where the station's firmware takes care of * responding to incoming Probe Request frames received from other P2P * Devices whilst in Listen state, rather than having the user space * wpa_supplicant do it. Information from received P2P requests are * forwarded from firmware to host whenever the host processor wakes up. + * @QCA_WLAN_VENDOR_FEATURE_OCE_STA: Device supports all OCE non-AP STA + * specific features. + * @QCA_WLAN_VENDOR_FEATURE_OCE_AP: Device supports all OCE AP specific + * features. + * @QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON: Device supports OCE STA-CFON + * specific features only. If a Device sets this bit but not the + * %QCA_WLAN_VENDOR_FEATURE_OCE_AP, the userspace shall assume that + * this Device may not support all OCE AP functionalities but can support + * only OCE STA-CFON functionalities. + * @QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY: Device supports self + * managed regulatory. * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits */ enum qca_wlan_vendor_features { @@ -507,6 +1001,10 @@ enum qca_wlan_vendor_features { QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY = 1, QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS = 2, QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD = 3, + QCA_WLAN_VENDOR_FEATURE_OCE_STA = 4, + QCA_WLAN_VENDOR_FEATURE_OCE_AP = 5, + QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON = 6, + QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY = 7, NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */ }; @@ -532,6 +1030,112 @@ enum qca_wlan_vendor_attr_data_offload_ind { QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST - 1 }; +/** + * enum qca_wlan_vendor_attr_ocb_set_config - Vendor subcmd attributes to set + * OCB config + * + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT: Number of channels in the + * configuration + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE: Size of the schedule + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY: Array of channels + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY: Array of channels to be + * scheduled + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY: Array of NDL channel + * information + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY: Array of NDL + * active state configuration + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS: Configuration flags such as + * OCB_CONFIG_FLAG_80211_FRAME_MODE + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_DEF_TX_PARAM: Default TX parameters to + * use in the case that a packet is sent without a TX control header + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_TA_MAX_DURATION: Max duration after the + * last TA received that the local time set by TA is synchronous to other + * communicating OCB STAs. + */ +enum qca_wlan_vendor_attr_ocb_set_config { + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT = 1, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE = 2, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY = 3, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY = 4, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY = 5, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY = 6, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS = 7, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_DEF_TX_PARAM = 8, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_TA_MAX_DURATION = 9, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX = + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_ocb_set_utc_time - Vendor subcmd attributes to set + * UTC time + * + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE: The UTC time as an array of + * 10 bytes + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR: The time error as an array of + * 5 bytes + */ +enum qca_wlan_vendor_attr_ocb_set_utc_time { + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE = 1, + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR = 2, + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX = + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_ocb_start_timing_advert - Vendor subcmd attributes + * to start sending timing advert frames + * + * @QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ: Cannel frequency + * on which to send the frames + * @QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE: Number of times + * the frame is sent in 5 seconds + */ +enum qca_wlan_vendor_attr_ocb_start_timing_advert { + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ = 1, + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE = 2, + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX = + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_ocb_stop_timing_advert - Vendor subcmd attributes + * to stop timing advert + * + * @QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ: The channel + * frequency on which to stop the timing advert + */ +enum qca_wlan_vendor_attr_ocb_stop_timing_advert { + QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ = 1, + QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX = + QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_ocb_get_tsf_response - Vendor subcmd attributes to + * get TSF timer value + * + * @QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH: Higher 32 bits of the + * timer + * @QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW: Lower 32 bits of the timer + */ +enum qca_wlan_vendor_attr_ocb_get_tsf_resp { + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH = 1, + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW = 2, + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_MAX = + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_AFTER_LAST - 1 +}; + enum qca_vendor_attr_get_preferred_freq_list { QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_INVALID, /* A 32-unsigned value; the interface type/mode for which the preferred @@ -544,6 +1148,12 @@ enum qca_vendor_attr_get_preferred_freq_list { * from kernel space to user space. */ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST, + /* An array of nested values as per enum qca_wlan_vendor_attr_pcl + * attribute. Each element contains frequency (MHz), weight, and flag + * bit mask indicating how the frequency should be used in P2P + * negotiation; sent from kernel space to user space. + */ + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_WEIGHED_PCL, /* keep last */ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX = @@ -679,11 +1289,39 @@ enum qca_vendor_attr_wisa_cmd { * vendor specific element is defined by the latest P802.11ax draft. * Please note that the draft is still work in progress and this element * payload is subject to change. + * + * @QCA_VENDOR_ELEM_RAPS: RAPS element (OFDMA-based Random Access Parameter Set + * element). + * This element can be used for pre-standard publication testing of HE + * before P802.11ax draft assigns the element ID extension. The payload of + * this vendor specific element is defined by the latest P802.11ax draft + * (not including the Element ID Extension field). Please note that the + * draft is still work in progress and this element payload is subject to + * change. + * + * @QCA_VENDOR_ELEM_MU_EDCA_PARAMS: MU EDCA Parameter Set element. + * This element can be used for pre-standard publication testing of HE + * before P802.11ax draft assigns the element ID extension. The payload of + * this vendor specific element is defined by the latest P802.11ax draft + * (not including the Element ID Extension field). Please note that the + * draft is still work in progress and this element payload is subject to + * change. + * + * @QCA_VENDOR_ELEM_BSS_COLOR_CHANGE: BSS Color Change Announcement element. + * This element can be used for pre-standard publication testing of HE + * before P802.11ax draft assigns the element ID extension. The payload of + * this vendor specific element is defined by the latest P802.11ax draft + * (not including the Element ID Extension field). Please note that the + * draft is still work in progress and this element payload is subject to + * change. */ enum qca_vendor_element_id { QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0, QCA_VENDOR_ELEM_HE_CAPAB = 1, QCA_VENDOR_ELEM_HE_OPER = 2, + QCA_VENDOR_ELEM_RAPS = 3, + QCA_VENDOR_ELEM_MU_EDCA_PARAMS = 4, + QCA_VENDOR_ELEM_BSS_COLOR_CHANGE = 5, }; /** @@ -696,29 +1334,32 @@ enum qca_vendor_element_id { * @QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES: Nested array attribute of supported * rates to be included * @QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE: flag used to send probe requests - * at non CCK rate in 2GHz band + * at non CCK rate in 2GHz band * @QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS: Unsigned 32-bit scan flags * @QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE: Unsigned 64-bit cookie provided by the - * driver for the specific scan request + * driver for the specific scan request * @QCA_WLAN_VENDOR_ATTR_SCAN_STATUS: Unsigned 8-bit status of the scan - * request decoded as in enum scan_status + * request decoded as in enum scan_status * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC: 6-byte MAC address to use when randomisation - * scan flag is set + * scan flag is set * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK: 6-byte MAC address mask to be used with - * randomisation + * randomisation + * @QCA_WLAN_VENDOR_ATTR_SCAN_BSSID: 6-byte MAC address representing the + * specific BSSID to scan for. */ enum qca_wlan_vendor_attr_scan { QCA_WLAN_VENDOR_ATTR_SCAN_INVALID_PARAM = 0, - QCA_WLAN_VENDOR_ATTR_SCAN_IE, - QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES, - QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS, - QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES, - QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE, - QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS, - QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE, - QCA_WLAN_VENDOR_ATTR_SCAN_STATUS, - QCA_WLAN_VENDOR_ATTR_SCAN_MAC, - QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK, + QCA_WLAN_VENDOR_ATTR_SCAN_IE = 1, + QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES = 2, + QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS = 3, + QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES = 4, + QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE = 5, + QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS = 6, + QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE = 7, + QCA_WLAN_VENDOR_ATTR_SCAN_STATUS = 8, + QCA_WLAN_VENDOR_ATTR_SCAN_MAC = 9, + QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK = 10, + QCA_WLAN_VENDOR_ATTR_SCAN_BSSID = 11, QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_SCAN_MAX = QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST - 1 @@ -726,10 +1367,10 @@ enum qca_wlan_vendor_attr_scan { /** * enum scan_status - Specifies the valid values the vendor scan attribute - * QCA_WLAN_VENDOR_ATTR_SCAN_STATUS can take + * QCA_WLAN_VENDOR_ATTR_SCAN_STATUS can take * * @VENDOR_SCAN_STATUS_NEW_RESULTS: implies the vendor scan is successful with - * new scan results + * new scan results * @VENDOR_SCAN_STATUS_ABORTED: implies the vendor scan was aborted in-between */ enum scan_status { @@ -776,7 +1417,8 @@ enum qca_vendor_attr_txpower_scale { enum qca_vendor_attr_txpower_decr_db { QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_INVALID, /* 8-bit unsigned value to indicate the reduction of TX power in dB for - * a virtual interface. */ + * a virtual interface. + */ QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB, /* keep last */ QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST, @@ -826,29 +1468,37 @@ enum qca_wlan_vendor_attr_config { */ QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND = 7, /* 8-bit unsigned value to configure the maximum TX MPDU for - * aggregation. */ + * aggregation. + */ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION = 8, /* 8-bit unsigned value to configure the maximum RX MPDU for - * aggregation. */ + * aggregation. + */ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION = 9, /* 8-bit unsigned value to configure the Non aggregrate/11g sw - * retry threshold (0 disable, 31 max). */ + * retry threshold (0 disable, 31 max). + */ QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY = 10, /* 8-bit unsigned value to configure the aggregrate sw - * retry threshold (0 disable, 31 max). */ + * retry threshold (0 disable, 31 max). + */ QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY = 11, /* 8-bit unsigned value to configure the MGMT frame - * retry threshold (0 disable, 31 max). */ + * retry threshold (0 disable, 31 max). + */ QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY = 12, /* 8-bit unsigned value to configure the CTRL frame - * retry threshold (0 disable, 31 max). */ + * retry threshold (0 disable, 31 max). + */ QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY = 13, /* 8-bit unsigned value to configure the propagation delay for - * 2G/5G band (0~63, units in us) */ + * 2G/5G band (0~63, units in us) + */ QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY = 14, /* Unsigned 32-bit value to configure the number of unicast TX fail * packet count. The peer is disconnected once this threshold is - * reached. */ + * reached. + */ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT = 15, /* Attribute used to set scan default IEs to the driver. * @@ -859,7 +1509,8 @@ enum qca_wlan_vendor_attr_config { * merged with the IEs received along with scan request coming to the * driver. If a particular IE is present in the scan default IEs but not * present in the scan request, then that IE should be added to the IEs - * sent in the Probe Request frames for that scan request. */ + * sent in the Probe Request frames for that scan request. + */ QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES = 16, /* Unsigned 32-bit attribute for generic commands */ QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_COMMAND = 17, @@ -868,42 +1519,183 @@ enum qca_wlan_vendor_attr_config { /* Unsigned 32-bit data attribute for generic command response */ QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA = 19, /* Unsigned 32-bit length attribute for - * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA */ + * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA + */ QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_LENGTH = 20, /* Unsigned 32-bit flags attribute for - * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA */ + * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA + */ QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_FLAGS = 21, /* Unsigned 32-bit, defining the access policy. * See enum qca_access_policy. Used with - * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST. */ + * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST. + */ QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY = 22, /* Sets the list of full set of IEs for which a specific access policy * has to be applied. Used along with * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY to control the access. - * Zero length payload can be used to clear this access constraint. */ + * Zero length payload can be used to clear this access constraint. + */ QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST = 23, /* Unsigned 32-bit, specifies the interface index (netdev) for which the * corresponding configurations are applied. If the interface index is * not specified, the configurations are attributed to the respective - * wiphy. */ + * wiphy. + */ QCA_WLAN_VENDOR_ATTR_CONFIG_IFINDEX = 24, /* 8-bit unsigned value to trigger QPower: 1-Enable, 0-Disable */ QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER = 25, /* 8-bit unsigned value to configure the driver and below layers to * ignore the assoc disallowed set by APs while connecting - * 1-Ignore, 0-Don't ignore */ + * 1-Ignore, 0-Don't ignore + */ QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED = 26, /* 32-bit unsigned value to trigger antenna diversity features: - * 1-Enable, 0-Disable */ + * 1-Enable, 0-Disable + */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA = 27, /* 32-bit unsigned value to configure specific chain antenna */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN = 28, /* 32-bit unsigned value to trigger cycle selftest - * 1-Enable, 0-Disable */ + * 1-Enable, 0-Disable + */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST = 29, /* 32-bit unsigned to configure the cycle time of selftest - * the unit is micro-second */ + * the unit is micro-second + */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL = 30, + /* 32-bit unsigned value to set reorder timeout for AC_VO */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE = 31, + /* 32-bit unsigned value to set reorder timeout for AC_VI */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO = 32, + /* 32-bit unsigned value to set reorder timeout for AC_BE */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT = 33, + /* 32-bit unsigned value to set reorder timeout for AC_BK */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND = 34, + /* 6-byte MAC address to point out the specific peer */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC = 35, + /* 32-bit unsigned value to set window size for specific peer */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT = 36, + /* 8-bit unsigned value to set the beacon miss threshold in 2.4 GHz */ + QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_24 = 37, + /* 8-bit unsigned value to set the beacon miss threshold in 5 GHz */ + QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_5 = 38, + /* 32-bit unsigned value to configure 5 or 10 MHz channel width for + * station device while in disconnect state. The attribute use the + * value of enum nl80211_chan_width: NL80211_CHAN_WIDTH_5 means 5 MHz, + * NL80211_CHAN_WIDTH_10 means 10 MHz. If set, the device work in 5 or + * 10 MHz channel width, the station will not connect to a BSS using 20 + * MHz or higher bandwidth. Set to NL80211_CHAN_WIDTH_20_NOHT to + * clear this constraint. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH = 39, + /* 32-bit unsigned value to configure the propagation absolute delay + * for 2G/5G band (units in us) + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY = 40, + /* 32-bit unsigned value to set probe period */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD = 41, + /* 32-bit unsigned value to set stay period */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD = 42, + /* 32-bit unsigned value to set snr diff */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SNR_DIFF = 43, + /* 32-bit unsigned value to set probe dwell time */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_DWELL_TIME = 44, + /* 32-bit unsigned value to set mgmt snr weight */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT = 45, + /* 32-bit unsigned value to set data snr weight */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT = 46, + /* 32-bit unsigned value to set ack snr weight */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT = 47, + /* 32-bit unsigned value to configure the listen interval. + * This is in units of beacon intervals. This configuration alters + * the negotiated listen interval with the AP during the connection. + * It is highly recommended to configure a value less than or equal to + * the one negotiated during the association. Configuring any greater + * value can have adverse effects (frame loss, AP disassociating STA, + * etc.). + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_LISTEN_INTERVAL = 48, + /* + * 8 bit unsigned value that is set on an AP/GO virtual interface to + * disable operations that would cause the AP/GO to leave its operating + * channel. + * + * This will restrict the scans to the AP/GO operating channel and the + * channels of the other band, if DBS is supported.A STA/CLI interface + * brought up after this setting is enabled, will be restricted to + * connecting to devices only on the AP/GO interface's operating channel + * or on the other band in DBS case. P2P supported channel list is + * modified, to only include AP interface's operating-channel and the + * channels of the other band if DBS is supported. + * + * These restrictions are only applicable as long as the AP/GO interface + * is alive. If the AP/GO interface is brought down then this + * setting/restriction is forgotten. + * + * If this variable is set on an AP/GO interface while a multi-channel + * concurrent session is active, it has no effect on the operation of + * the current interfaces, other than restricting the scan to the AP/GO + * operating channel and the other band channels if DBS is supported. + * However, if the STA is brought down and restarted then the new STA + * connection will either be formed on the AP/GO channel or on the + * other band in a DBS case. This is because of the scan being + * restricted on these channels as mentioned above. + * + * 1-Restrict / 0-Don't restrict offchannel operations. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RESTRICT_OFFCHANNEL = 49, + /* + * 8 bit unsigned value to enable/disable LRO (Large Receive Offload) + * on an interface. + * 1 - Enable, 0 - Disable. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_LRO = 50, + + /* + * 8 bit unsigned value to globally enable/disable scan + * 1 - Enable, 0 - Disable. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_ENABLE = 51, + + /* 8-bit unsigned value to set the total beacon miss count + * This parameter will set the total beacon miss count. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_TOTAL_BEACON_MISS_COUNT = 52, + + /* Unsigned 32-bit value to configure the number of continuous + * Beacon Miss which shall be used by the firmware to penalize + * the RSSI for BTC. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_PENALIZE_AFTER_NCONS_BEACON_MISS_BTC = 53, + + /* 8-bit unsigned value to configure the driver and below layers to + * enable/disable all FILS features. + * 0-enable, 1-disable + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS = 54, + + /* 16-bit unsigned value to configure the level of WLAN latency + * module. See enum qca_wlan_vendor_attr_config_latency_level. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL = 55, + + /* 8-bit unsigned value indicating the driver to use the RSNE as-is from + * the connect interface. Exclusively used for the scenarios where the + * device is used as a test bed device with special functionality and + * not recommended for production. This helps driver to not validate the + * RSNE passed from user space and thus allow arbitrary IE data to be + * used for testing purposes. + * 1-enable, 0-disable. + * Applications set/reset this configuration. If not reset, this + * parameter remains in use until the driver is unloaded. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RSN_IE = 56, + + /* 8-bit unsigned value to trigger green Tx power saving. + * 1-Enable, 0-Disable + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_GTX = 57, /* keep last */ QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST, @@ -937,7 +1729,8 @@ enum qca_wlan_vendor_attr_sap_config { enum qca_wlan_vendor_attr_sap_conditional_chan_switch { QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_INVALID = 0, /* Priority based frequency list (an array of u32 values in host byte - * order) */ + * order) + */ QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST = 1, /* Status of the conditional switch (u32). * 0: Success, Non-zero: Failure @@ -972,6 +1765,36 @@ enum qca_wlan_gpio_attr { }; /** + * qca_wlan_set_qdepth_thresh_attr - Parameters for setting + * MSDUQ depth threshold per peer per tid in the target + * + * Associated Vendor Command: + * QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH + */ +enum qca_wlan_set_qdepth_thresh_attr { + QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_INVALID = 0, + /* 6-byte MAC address */ + QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_MAC_ADDR, + /* Unsigned 32-bit attribute for holding the TID */ + QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_TID, + /* Unsigned 32-bit attribute for holding the update mask + * bit 0 - Update high priority msdu qdepth threshold + * bit 1 - Update low priority msdu qdepth threshold + * bit 2 - Update UDP msdu qdepth threshold + * bit 3 - Update Non UDP msdu qdepth threshold + * rest of bits are reserved + */ + QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_UPDATE_MASK, + /* Unsigned 32-bit attribute for holding the threshold value */ + QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_VALUE, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_LAST, + QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_MAX = + QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_LAST - 1, +}; + +/** * enum qca_wlan_vendor_attr_get_hw_capability - Wi-Fi hardware capability */ enum qca_wlan_vendor_attr_get_hw_capability { @@ -1143,6 +1966,12 @@ enum qca_wlan_vendor_attr_get_hw_capability { * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF: per antenna NF value * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON: RSSI of beacon * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON: SNR of beacon + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_REPORT_TIME: u64 + * Absolute timestamp from 1970/1/1, unit in ms. After receiving the + * message, user layer APP could call gettimeofday to get another + * timestamp and calculate transfer delay for the message. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MEASUREMENT_TIME: u32 + * Real period for this measurement, unit in us. */ enum qca_wlan_vendor_attr_ll_stats_ext { QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_INVALID = 0, @@ -1236,6 +2065,9 @@ enum qca_wlan_vendor_attr_ll_stats_ext { QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_REPORT_TIME, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MEASUREMENT_TIME, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX = QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST - 1 @@ -1289,7 +2121,7 @@ enum qca_wlan_vendor_attr_loc_capa { * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR: Set if driver * can run FTM sessions. QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION * will be supported if set. -* @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP: Set if FTM responder + * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP: Set if FTM responder * supports immediate (ASAP) response. * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA: Set if driver supports standalone * AOA measurement using QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS. @@ -1320,6 +2152,10 @@ enum qca_wlan_vendor_attr_loc_capa_flags { * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD: Request AOA * measurement every <value> bursts. If 0 or not specified, * AOA measurements will be disabled for this peer. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ: Frequency in MHz where + * the measurement frames are exchanged. Optional; if not + * specified, try to locate the peer in the kernel scan + * results cache and use frequency from there. */ enum qca_wlan_vendor_attr_ftm_peer_info { QCA_WLAN_VENDOR_ATTR_FTM_PEER_INVALID, @@ -1328,6 +2164,7 @@ enum qca_wlan_vendor_attr_ftm_peer_info { QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS, QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID, QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ, /* keep last */ QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX = @@ -1587,4 +2424,3824 @@ enum qca_wlan_vendor_attr_encryption_test { QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_AFTER_LAST - 1 }; +/** + * enum qca_wlan_vendor_attr_dmg_rf_sector_type - Type of + * sector for DMG RF sector operations. + * + * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_RX: RX sector + * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_TX: TX sector + */ +enum qca_wlan_vendor_attr_dmg_rf_sector_type { + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_RX, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_TX, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_MAX +}; + +/** + * BRP antenna limit mode + * + * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE: Disable BRP force + * antenna limit, BRP will be performed as usual. + * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_EFFECTIVE: Define maximal + * antennas limit. the hardware may use less antennas than the + * maximum limit. + * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_FORCE: The hardware will + * use exactly the specified number of antennas for BRP. + */ +enum qca_wlan_vendor_attr_brp_ant_limit_mode { + QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE, + QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_EFFECTIVE, + QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_FORCE, + QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_MAX +}; + +/** + * enum qca_wlan_vendor_attr_dmg_rf_sector_cfg - Attributes for + * DMG RF sector configuration for a single RF module. + * The values are defined in a compact way which closely matches + * the way it is stored in HW registers. + * The configuration provides values for 32 antennas and 8 distribution + * amplifiers, and together describes the characteristics of the RF + * sector - such as a beam in some direction with some gain. + * + * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX: Index + * of RF module for this configuration. + * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE0: Bit 0 of edge + * amplifier gain index. Unsigned 32 bit number containing + * bits for all 32 antennas. + * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE1: Bit 1 of edge + * amplifier gain index. Unsigned 32 bit number containing + * bits for all 32 antennas. + * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE2: Bit 2 of edge + * amplifier gain index. Unsigned 32 bit number containing + * bits for all 32 antennas. + * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_HI: Phase values + * for first 16 antennas, 2 bits per antenna. + * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_LO: Phase values + * for last 16 antennas, 2 bits per antenna. + * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16: Contains + * DTYPE values (3 bits) for each distribution amplifier, followed + * by X16 switch bits for each distribution amplifier. There are + * total of 8 distribution amplifiers. + */ +enum qca_wlan_vendor_attr_dmg_rf_sector_cfg { + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX = 1, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE0 = 2, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE1 = 3, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE2 = 4, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_HI = 5, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_LO = 6, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16 = 7, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MAX = + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1 +}; + +enum qca_wlan_vendor_attr_ll_stats_set { + QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_INVALID = 0, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD = 1, + QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING = 2, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX = + QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_AFTER_LAST - 1, +}; + +enum qca_wlan_vendor_attr_ll_stats_clr { + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_INVALID = 0, + /* Unsigned 32bit bitmap for clearing statistics + * All radio statistics 0x00000001 + * cca_busy_time (within radio statistics) 0x00000002 + * All channel stats (within radio statistics) 0x00000004 + * All scan statistics (within radio statistics) 0x00000008 + * All interface statistics 0x00000010 + * All tx rate statistics (within interface statistics) 0x00000020 + * All ac statistics (with in interface statistics) 0x00000040 + * All contention (min, max, avg) statistics (within ac statisctics) + * 0x00000080. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK = 1, + /* Unsigned 8 bit value: Request to stop statistics collection */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ = 2, + + /* Unsigned 32 bit bitmap: Response from the driver + * for the cleared statistics + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK = 3, + /* Unsigned 8 bit value: Response from driver/firmware + * for the stop request + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP = 4, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX = + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_AFTER_LAST - 1, +}; + +enum qca_wlan_vendor_attr_ll_stats_get { + QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_INVALID = 0, + /* Unsigned 32 bit value provided by the caller issuing the GET stats + * command. When reporting the stats results, the driver uses the same + * value to indicate which GET request the results correspond to. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID = 1, + /* Unsigned 32 bit value - bit mask to identify what statistics are + * requested for retrieval. + * Radio Statistics 0x00000001 + * Interface Statistics 0x00000020 + * All Peer Statistics 0x00000040 + * Peer Statistics 0x00000080 + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK = 2, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX = + QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_AFTER_LAST - 1, +}; + +enum qca_wlan_vendor_attr_ll_stats_results { + QCA_WLAN_VENDOR_ATTR_LL_STATS_INVALID = 0, + /* Unsigned 32bit value. Used by the driver; must match the request id + * provided with the QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET command. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_REQ_ID = 1, + + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_BEACON_RX = 2, + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_RX = 3, + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_RX = 4, + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_TX = 5, + /* Signed 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_MGMT = 6, + /* Signed 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_DATA = 7, + /* Signed 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_ACK = 8, + + /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_* are + * nested within the interface stats. + */ + + /* Interface mode, e.g., STA, SOFTAP, IBSS, etc. + * Type = enum wifi_interface_mode. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MODE = 9, + /* Interface MAC address. An array of 6 Unsigned int8 */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR = 10, + /* Type = enum wifi_connection_state, e.g., DISCONNECTED, + * AUTHENTICATING, etc. valid for STA, CLI only. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_STATE = 11, + /* Type = enum wifi_roam_state. Roaming state, e.g., IDLE or ACTIVE + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_ROAMING = 12, + /* Unsigned 32 bit value. WIFI_CAPABILITY_XXX */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_CAPABILITIES = 13, + /* NULL terminated SSID. An array of 33 Unsigned 8bit values */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID = 14, + /* BSSID. An array of 6 unsigned 8 bit values */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID = 15, + /* Country string advertised by AP. An array of 3 unsigned 8 bit + * values. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR = 16, + /* Country string for this association. An array of 3 unsigned 8 bit + * values. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR = 17, + + /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_* could + * be nested within the interface stats. + */ + + /* Type = enum wifi_traffic_ac, e.g., V0, VI, BE and BK */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_AC = 18, + /* Unsigned int 32 value corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MPDU = 19, + /* Unsigned int 32 value corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MPDU = 20, + /* Unsigned int 32 value corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MCAST = 21, + /* Unsigned int 32 value corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MCAST = 22, + /* Unsigned int 32 value corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_AMPDU = 23, + /* Unsigned int 32 value corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_AMPDU = 24, + /* Unsigned int 32 value corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_MPDU_LOST = 25, + /* Unsigned int 32 value corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES = 26, + /* Unsigned int 32 value corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_SHORT = 27, + /* Unsigned int 32 values corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_LONG = 28, + /* Unsigned int 32 values corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MIN = 29, + /* Unsigned int 32 values corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MAX = 30, + /* Unsigned int 32 values corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_AVG = 31, + /* Unsigned int 32 values corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_NUM_SAMPLES = 32, + /* Unsigned 32 bit value. Number of peers */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS = 33, + + /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_* are + * nested within the interface stats. + */ + + /* Type = enum wifi_peer_type. Peer type, e.g., STA, AP, P2P GO etc. */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_TYPE = 34, + /* MAC addr corresponding to respective peer. An array of 6 unsigned + * 8 bit values. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS = 35, + /* Unsigned int 32 bit value representing capabilities corresponding + * to respective peer. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_CAPABILITIES = 36, + /* Unsigned 32 bit value. Number of rates */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES = 37, + + /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_* + * are nested within the rate stat. + */ + + /* Wi-Fi Rate - separate attributes defined for individual fields */ + + /* Unsigned int 8 bit value; 0: OFDM, 1:CCK, 2:HT 3:VHT 4..7 reserved */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_PREAMBLE = 38, + /* Unsigned int 8 bit value; 0:1x1, 1:2x2, 3:3x3, 4:4x4 */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_NSS = 39, + /* Unsigned int 8 bit value; 0:20 MHz, 1:40 MHz, 2:80 MHz, 3:160 MHz */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW = 40, + /* Unsigned int 8 bit value; OFDM/CCK rate code would be as per IEEE Std + * in the units of 0.5 Mbps HT/VHT it would be MCS index + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX = 41, + + /* Unsigned 32 bit value. Bit rate in units of 100 kbps */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE = 42, + + /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_STAT_* could be + * nested within the peer info stats. + */ + + /* Unsigned int 32 bit value. Number of successfully transmitted data + * packets, i.e., with ACK received corresponding to the respective + * rate. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_TX_MPDU = 43, + /* Unsigned int 32 bit value. Number of received data packets + * corresponding to the respective rate. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RX_MPDU = 44, + /* Unsigned int 32 bit value. Number of data packet losses, i.e., no ACK + * received corresponding to the respective rate. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MPDU_LOST = 45, + /* Unsigned int 32 bit value. Total number of data packet retries for + * the respective rate. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES = 46, + /* Unsigned int 32 bit value. Total number of short data packet retries + * for the respective rate. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_SHORT = 47, + /* Unsigned int 32 bit value. Total number of long data packet retries + * for the respective rate. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_LONG = 48, + + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID = 49, + /* Unsigned 32 bit value. Total number of msecs the radio is awake + * accruing over time. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME = 50, + /* Unsigned 32 bit value. Total number of msecs the radio is + * transmitting accruing over time. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME = 51, + /* Unsigned 32 bit value. Total number of msecs the radio is in active + * receive accruing over time. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME = 52, + /* Unsigned 32 bit value. Total number of msecs the radio is awake due + * to all scan accruing over time. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN = 53, + /* Unsigned 32 bit value. Total number of msecs the radio is awake due + * to NAN accruing over time. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD = 54, + /* Unsigned 32 bit value. Total number of msecs the radio is awake due + * to GSCAN accruing over time. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN = 55, + /* Unsigned 32 bit value. Total number of msecs the radio is awake due + * to roam scan accruing over time. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN = 56, + /* Unsigned 32 bit value. Total number of msecs the radio is awake due + * to PNO scan accruing over time. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN = 57, + /* Unsigned 32 bit value. Total number of msecs the radio is awake due + * to Hotspot 2.0 scans and GAS exchange accruing over time. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20 = 58, + /* Unsigned 32 bit value. Number of channels. */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS = 59, + + /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_* could + * be nested within the channel stats. + */ + + /* Type = enum wifi_channel_width. Channel width, e.g., 20, 40, 80 */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH = 60, + /* Unsigned 32 bit value. Primary 20 MHz channel. */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ = 61, + /* Unsigned 32 bit value. Center frequency (MHz) first segment. */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0 = 62, + /* Unsigned 32 bit value. Center frequency (MHz) second segment. */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1 = 63, + + /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_* could be + * nested within the radio stats. + */ + + /* Unsigned int 32 bit value representing total number of msecs the + * radio is awake on that channel accruing over time, corresponding to + * the respective channel. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME = 64, + /* Unsigned int 32 bit value representing total number of msecs the CCA + * register is busy accruing over time corresponding to the respective + * channel. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME = 65, + + QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS = 66, + + /* Signifies the nested list of channel attributes + * QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_* + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO = 67, + + /* Signifies the nested list of peer info attributes + * QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_* + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO = 68, + + /* Signifies the nested list of rate info attributes + * QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_* + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO = 69, + + /* Signifies the nested list of wmm info attributes + * QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_* + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO = 70, + + /* Unsigned 8 bit value. Used by the driver; if set to 1, it indicates + * that more stats, e.g., peers or radio, are to follow in the next + * QCA_NL80211_VENDOR_SUBCMD_LL_STATS_*_RESULTS event. + * Otherwise, it is set to 0. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA = 71, + + /* Unsigned 64 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_AVERAGE_TSF_OFFSET = 72, + + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_DETECTED = 73, + + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_AVG_NUM_FRAMES_LEAKED = 74, + + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_GUARD_TIME = 75, + + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE = 76, + + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_TX_LEVELS = 77, + + /* Number of msecs the radio spent in transmitting for each power level + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL = 78, + + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_SUCC_CNT = 79, + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_FAIL_CNT = 80, + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_SUCC_CNT = 81, + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_FAIL_CNT = 82, + + /* Unsigned int 32 value. + * Pending MSDUs corresponding to respective AC. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_PENDING_MSDU = 83, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX = + QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST - 1, +}; + +enum qca_wlan_vendor_attr_ll_stats_type { + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_INVALID = 0, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_RADIO = 1, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_IFACE = 2, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_PEERS = 3, + + /* keep last */ + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_AFTER_LAST, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_MAX = + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_tdls_configuration - Attributes for + * TDLS configuration to the host driver. + * + * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE: Configure the TDLS trigger + * mode in the host driver. enum qca_wlan_vendor_tdls_trigger_mode + * represents the different TDLS trigger modes. + * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD: Duration (u32) within + * which QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD number + * of packets shall meet the criteria for implicit TDLS setup. + * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD: Number (u32) of Tx/Rx packets + * within a duration QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD + * to initiate a TDLS setup. + * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_DISCOVERY_PERIOD: Time (u32) to initiate + * a TDLS Discovery to the peer. + * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX_DISCOVERY_ATTEMPT: Max number (u32) of + * discovery attempts to know the TDLS capability of the peer. A peer is + * marked as TDLS not capable if there is no response for all the attempts. + * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT: Represents a duration (u32) + * within which QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD + * number of TX / RX frames meet the criteria for TDLS teardown. + * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD: Minimum number (u32) + * of Tx/Rx packets within a duration + * QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT to tear down a TDLS link. + * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_SETUP_RSSI_THRESHOLD: Threshold + * corresponding to the RSSI of the peer below which a TDLS setup is + * triggered. + * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TEARDOWN_RSSI_THRESHOLD: Threshold + * corresponding to the RSSI of the peer above which a TDLS teardown is + * triggered. + */ +enum qca_wlan_vendor_attr_tdls_configuration { + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE = 1, + + /* Attributes configuring the TDLS Implicit Trigger */ + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD = 2, + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD = 3, + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_DISCOVERY_PERIOD = 4, + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX_DISCOVERY_ATTEMPT = 5, + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT = 6, + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD = 7, + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_SETUP_RSSI_THRESHOLD = 8, + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TEARDOWN_RSSI_THRESHOLD = 9, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX = + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_tdls_trigger_mode: Represents the TDLS trigger mode in + * the driver + * + * The following are the different values for + * QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE. + * + * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT: The trigger to initiate/teardown + * the TDLS connection to a respective peer comes from the user space. + * wpa_supplicant provides the commands TDLS_SETUP, TDLS_TEARDOWN, + * TDLS_DISCOVER to do this. + * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT: Host driver triggers this TDLS + * setup/teardown to the eligible peer once the configured criteria + * (such as TX/RX threshold, RSSI) is met. The attributes + * in QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IMPLICIT_PARAMS correspond to + * the different configuration criteria for the TDLS trigger from the + * host driver. + * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL: Enables the driver to trigger + * the TDLS setup / teardown through the implicit mode only to the + * configured MAC addresses (wpa_supplicant, with tdls_external_control=1, + * configures the MAC address through TDLS_SETUP / TDLS_TEARDOWN commands). + * External mode works on top of the implicit mode. Thus the host driver + * is expected to configure in TDLS Implicit mode too to operate in + * External mode. + * Configuring External mode alone without Implicit mode is invalid. + * + * All the above implementations work as expected only when the host driver + * advertises the capability WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP - representing + * that the TDLS message exchange is not internal to the host driver, but + * depends on wpa_supplicant to do the message exchange. + */ +enum qca_wlan_vendor_tdls_trigger_mode { + QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT = 1 << 0, + QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT = 1 << 1, + QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL = 1 << 2, +}; + +/** + * enum qca_vendor_attr_sar_limits_selections - Source of SAR power limits + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0: Select SAR profile #0 + * that is hard-coded in the Board Data File (BDF). + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1: Select SAR profile #1 + * that is hard-coded in the Board Data File (BDF). + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2: Select SAR profile #2 + * that is hard-coded in the Board Data File (BDF). + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3: Select SAR profile #3 + * that is hard-coded in the Board Data File (BDF). + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4: Select SAR profile #4 + * that is hard-coded in the Board Data File (BDF). + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE: Do not select any + * source of SAR power limits, thereby disabling the SAR power + * limit feature. + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER: Select the SAR power + * limits configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR. + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0: Select the SAR power + * limits version 2.0 configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR. + * + * This enumerates the valid set of values that may be supplied for + * attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT in an instance of + * the %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor command or in + * the response to an instance of the + * %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS vendor command. + */ +enum qca_vendor_attr_sar_limits_selections { + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0 = 0, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1 = 1, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2 = 2, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3 = 3, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4 = 4, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE = 5, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER = 6, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0 = 7, +}; + +/** + * enum qca_vendor_attr_sar_limits_spec_modulations - + * SAR limits specification modulation + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK - + * CCK modulation + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM - + * OFDM modulation + * + * This enumerates the valid set of values that may be supplied for + * attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION in an + * instance of attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC in an + * instance of the %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor + * command or in the response to an instance of the + * %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS vendor command. + */ +enum qca_vendor_attr_sar_limits_spec_modulations { + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK = 0, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM = 1, +}; + +/** + * enum qca_vendor_attr_sar_limits - Attributes for SAR power limits + * + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE: Optional (u32) value to + * select which SAR power limit table should be used. Valid + * values are enumerated in enum + * %qca_vendor_attr_sar_limits_selections. The existing SAR + * power limit selection is unchanged if this attribute is not + * present. + * + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS: Optional (u32) value + * which specifies the number of SAR power limit specifications + * which will follow. + * + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC: Nested array of SAR power + * limit specifications. The number of specifications is + * specified by @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS. Each + * specification contains a set of + * QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_* attributes. A + * specification is uniquely identified by the attributes + * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND, + * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN, and + * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION and always + * contains as a payload the attribute + * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT, + * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX. + * Either %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT or + * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX is + * needed based upon the value of + * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE. + * + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND: Optional (u32) value to + * indicate for which band this specification applies. Valid + * values are enumerated in enum %nl80211_band (although not all + * bands may be supported by a given device). If the attribute is + * not supplied then the specification will be applied to all + * supported bands. + * + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN: Optional (u32) value + * to indicate for which antenna chain this specification + * applies, i.e. 1 for chain 1, 2 for chain 2, etc. If the + * attribute is not supplied then the specification will be + * applied to all chains. + * + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION: Optional (u32) + * value to indicate for which modulation scheme this + * specification applies. Valid values are enumerated in enum + * %qca_vendor_attr_sar_limits_spec_modulations. If the attribute + * is not supplied then the specification will be applied to all + * modulation schemes. + * + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT: Required (u32) + * value to specify the actual power limit value in units of 0.5 + * dBm (i.e., a value of 11 represents 5.5 dBm). + * This is required, when %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT is + * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER. + * + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX: Required (u32) + * value to indicate SAR V2 indices (0 - 11) to select SAR V2 profiles. + * This is required, when %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT is + * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0. + * + * These attributes are used with %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS + * and %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS. + */ +enum qca_vendor_attr_sar_limits { + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE = 1, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS = 2, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC = 3, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND = 4, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN = 5, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION = 6, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT = 7, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX = 8, + + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX = + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_get_wifi_info: Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO sub command. + */ +enum qca_wlan_vendor_attr_get_wifi_info { + QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION = 1, + QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION = 2, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX = + QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_AFTER_LAST - 1, +}; + +/* + * enum qca_wlan_vendor_attr_wifi_logger_start: Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START sub command. + */ +enum qca_wlan_vendor_attr_wifi_logger_start { + QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID = 1, + QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL = 2, + QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS = 3, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_GET_MAX = + QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_AFTER_LAST - 1, +}; + +enum qca_wlan_vendor_attr_logger_results { + QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_INVALID = 0, + + /* Unsigned 32-bit value; must match the request Id supplied by + * Wi-Fi HAL in the corresponding subcmd NL msg. + */ + QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_REQUEST_ID = 1, + + /* Unsigned 32-bit value; used to indicate the size of memory + * dump to be allocated. + */ + QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MEMDUMP_SIZE = 2, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MAX = + QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_AFTER_LAST - 1, +}; + +enum qca_wlan_vendor_attr_roaming_config_params { + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_INVALID = 0, + + QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD = 1, + QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID = 2, + + /* Attributes for wifi_set_ssid_white_list */ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS = 3, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST = 4, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID = 5, + + /* Attributes for set_roam_params */ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD = 6, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD = 7, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR = 8, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR = 9, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST = 10, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS = 11, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER = 12, + + /* Attribute for set_lazy_roam */ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE = 13, + + /* Attribute for set_lazy_roam with preferences */ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS = 14, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID = 15, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID = 16, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER = 17, + + /* Attribute for set_blacklist bssid params */ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS = 18, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID = 19, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID = 20, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX = + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST - 1, +}; + +/* + * enum qca_wlan_vendor_attr_roam_subcmd: Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_ROAM sub command. + */ +enum qca_wlan_vendor_attr_roam_subcmd { + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SSID_WHITE_LIST = 1, + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_GSCAN_ROAM_PARAMS = 2, + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_LAZY_ROAM = 3, + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BSSID_PREFS = 4, + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BSSID_PARAMS = 5, + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID = 6, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_MAX = + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_AFTER_LAST - 1, +}; + +enum qca_wlan_vendor_attr_gscan_config_params { + QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_INVALID = 0, + + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID = 1, + + /* Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_VALID_CHANNELS sub command. + */ + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND + = 2, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS + = 3, + + /* Attributes for input params used by + * QCA_NL80211_VENDOR_SUBCMD_GSCAN_START sub command. + */ + + /* Unsigned 32-bit value; channel frequency */ + QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_CHANNEL = 4, + /* Unsigned 32-bit value; dwell time in ms. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_DWELL_TIME = 5, + /* Unsigned 8-bit value; 0: active; 1: passive; N/A for DFS */ + QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_PASSIVE = 6, + /* Unsigned 8-bit value; channel class */ + QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_CLASS = 7, + + /* Unsigned 8-bit value; bucket index, 0 based */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_INDEX = 8, + /* Unsigned 8-bit value; band. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_BAND = 9, + /* Unsigned 32-bit value; desired period, in ms. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_PERIOD = 10, + /* Unsigned 8-bit value; report events semantics. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_REPORT_EVENTS = 11, + /* Unsigned 32-bit value. Followed by a nested array of + * GSCAN_CHANNEL_SPEC_* attributes. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS = 12, + + /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_* attributes. + * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC = 13, + + /* Unsigned 32-bit value; base timer period in ms. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_BASE_PERIOD = 14, + /* Unsigned 32-bit value; number of APs to store in each scan in the + * BSSID/RSSI history buffer (keep the highest RSSI APs). + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN = 15, + /* Unsigned 8-bit value; in %, when scan buffer is this much full, wake + * up AP. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT + = 16, + + /* Unsigned 8-bit value; number of scan bucket specs; followed by a + * nested array of_GSCAN_BUCKET_SPEC_* attributes and values. The size + * of the array is determined by NUM_BUCKETS. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS = 17, + + /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_* attributes. + * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC = 18, + + /* Unsigned 8-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH + = 19, + /* Unsigned 32-bit value; maximum number of results to be returned. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_MAX + = 20, + + /* An array of 6 x unsigned 8-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_BSSID = 21, + /* Signed 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_RSSI_LOW = 22, + /* Signed 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH = 23, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_CHANNEL = 24, + + /* Number of hotlist APs as unsigned 32-bit value, followed by a nested + * array of AP_THRESHOLD_PARAM attributes and values. The size of the + * array is determined by NUM_AP. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BSSID_HOTLIST_PARAMS_NUM_AP = 25, + + /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_* attributes. + * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM = 26, + + /* Unsigned 32-bit value; number of samples for averaging RSSI. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE + = 27, + /* Unsigned 32-bit value; number of samples to confirm AP loss. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE + = 28, + /* Unsigned 32-bit value; number of APs breaching threshold. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING = 29, + /* Unsigned 32-bit value; number of APs. Followed by an array of + * AP_THRESHOLD_PARAM attributes. Size of the array is NUM_AP. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP = 30, + /* Unsigned 32-bit value; number of samples to confirm AP loss. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE + = 31, + /* Unsigned 32-bit value. If max_period is non zero or different than + * period, then this bucket is an exponential backoff bucket. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_MAX_PERIOD = 32, + /* Unsigned 32-bit value. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_BASE = 33, + /* Unsigned 32-bit value. For exponential back off bucket, number of + * scans to perform for a given period. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_STEP_COUNT = 34, + /* Unsigned 8-bit value; in number of scans, wake up AP after these + * many scans. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS + = 35, + + /* Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SSID_HOTLIST sub command. + */ + /* Unsigned 3-2bit value; number of samples to confirm SSID loss. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_LOST_SSID_SAMPLE_SIZE + = 36, + /* Number of hotlist SSIDs as unsigned 32-bit value, followed by a + * nested array of SSID_THRESHOLD_PARAM_* attributes and values. The + * size of the array is determined by NUM_SSID. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_NUM_SSID = 37, + /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_* + * attributes. + * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_NUM_SSID + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM = 38, + + /* An array of 33 x unsigned 8-bit value; NULL terminated SSID */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_SSID = 39, + /* Unsigned 8-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_BAND = 40, + /* Signed 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_RSSI_LOW = 41, + /* Signed 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_RSSI_HIGH = 42, + /* Unsigned 32-bit value; a bitmask with additional gscan config flag. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_CONFIGURATION_FLAGS = 43, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_MAX = + QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_AFTER_LAST - 1, +}; + +enum qca_wlan_vendor_attr_gscan_results { + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_INVALID = 0, + + /* Unsigned 32-bit value; must match the request Id supplied by + * Wi-Fi HAL in the corresponding subcmd NL msg. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_REQUEST_ID = 1, + + /* Unsigned 32-bit value; used to indicate the status response from + * firmware/driver for the vendor sub-command. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_STATUS = 2, + + /* GSCAN Valid Channels attributes */ + /* Unsigned 32bit value; followed by a nested array of CHANNELS. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_CHANNELS = 3, + /* An array of NUM_CHANNELS x unsigned 32-bit value integers + * representing channel numbers. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CHANNELS = 4, + + /* GSCAN Capabilities attributes */ + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE = 5, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS = 6, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN + = 7, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE + = 8, + /* Signed 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD + = 9, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS = 10, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS + = 11, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES + = 12, + + /* GSCAN Attributes used with + * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_RESULTS_AVAILABLE sub-command. + */ + + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE = 13, + + /* GSCAN attributes used with + * QCA_NL80211_VENDOR_SUBCMD_GSCAN_FULL_SCAN_RESULT sub-command. + */ + + /* An array of NUM_RESULTS_AVAILABLE x + * QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_* + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST = 14, + + /* Unsigned 64-bit value; age of sample at the time of retrieval */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_TIME_STAMP = 15, + /* 33 x unsigned 8-bit value; NULL terminated SSID */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_SSID = 16, + /* An array of 6 x unsigned 8-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_BSSID = 17, + /* Unsigned 32-bit value; channel frequency in MHz */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_CHANNEL = 18, + /* Signed 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RSSI = 19, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RTT = 20, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RTT_SD = 21, + /* Unsigned 16-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_BEACON_PERIOD = 22, + /* Unsigned 16-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_CAPABILITY = 23, + /* Unsigned 32-bit value; size of the IE DATA blob */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_IE_LENGTH = 24, + /* An array of IE_LENGTH x unsigned 8-bit value; blob of all the + * information elements found in the beacon; this data should be a + * packed list of wifi_information_element objects, one after the + * other. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_IE_DATA = 25, + + /* Unsigned 8-bit value; set by driver to indicate more scan results are + * available. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_MORE_DATA = 26, + + /* GSCAN attributes for + * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_EVENT sub-command. + */ + /* Unsigned 8-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_EVENT_TYPE = 27, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_EVENT_STATUS = 28, + + /* GSCAN attributes for + * QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_FOUND sub-command. + */ + /* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE + * to indicate number of results. + * Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the + * list of results. + */ + + /* GSCAN attributes for + * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SIGNIFICANT_CHANGE sub-command. + */ + /* An array of 6 x unsigned 8-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_BSSID = 29, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_CHANNEL + = 30, + /* Unsigned 32-bit value. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_NUM_RSSI + = 31, + /* A nested array of signed 32-bit RSSI values. Size of the array is + * determined by (NUM_RSSI of SIGNIFICANT_CHANGE_RESULT_NUM_RSSI. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_RSSI_LIST + = 32, + + /* GSCAN attributes used with + * QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS sub-command. + */ + /* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE + * to indicate number of gscan cached results returned. + * Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_LIST to indicate + * the list of gscan cached results. + */ + + /* An array of NUM_RESULTS_AVAILABLE x + * QCA_NL80211_VENDOR_ATTR_GSCAN_CACHED_RESULTS_* + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_LIST = 33, + /* Unsigned 32-bit value; a unique identifier for the scan unit. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_SCAN_ID = 34, + /* Unsigned 32-bit value; a bitmask w/additional information about scan. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_FLAGS = 35, + /* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE + * to indicate number of wifi scan results/bssids retrieved by the scan. + * Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the + * list of wifi scan results returned for each cached result block. + */ + + /* GSCAN attributes for + * QCA_NL80211_VENDOR_SUBCMD_PNO_NETWORK_FOUND sub-command. + */ + /* Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE for + * number of results. + * Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the nested + * list of wifi scan results returned for each + * wifi_passpoint_match_result block. + * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE. + */ + + /* GSCAN attributes for + * QCA_NL80211_VENDOR_SUBCMD_PNO_PASSPOINT_NETWORK_FOUND sub-command. + */ + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_NETWORK_FOUND_NUM_MATCHES + = 36, + /* A nested array of + * QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_* + * attributes. Array size = + * *_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_NETWORK_FOUND_NUM_MATCHES. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_RESULT_LIST = 37, + + /* Unsigned 32-bit value; network block id for the matched network */ + QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ID = 38, + /* Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the nested + * list of wifi scan results returned for each + * wifi_passpoint_match_result block. + */ + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP_LEN = 39, + /* An array size of PASSPOINT_MATCH_ANQP_LEN of unsigned 8-bit values; + * ANQP data in the information_element format. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP = 40, + + /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS = 41, + /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS = 42, + /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID + = 43, + /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID + = 44, + + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_BUCKETS_SCANNED = 45, + + /* Unsigned 32-bit value; a GSCAN Capabilities attribute. + * This is used to limit the maximum number of BSSIDs while sending + * the vendor command QCA_NL80211_VENDOR_SUBCMD_ROAM with attributes + * QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID and + * QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_MAX_NUM_BLACKLISTED_BSSID = 46, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX = + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_AFTER_LAST - 1, +}; + +enum qca_wlan_vendor_attr_pno_config_params { + QCA_WLAN_VENDOR_ATTR_PNO_INVALID = 0, + /* Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_PNO_SET_PASSPOINT_LIST sub command. + */ + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM = 1, + /* Array of nested QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_* + * attributes. Array size = + * QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM. + */ + QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NETWORK_ARRAY = 2, + + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID = 3, + /* An array of 256 x unsigned 8-bit value; NULL terminated UTF-8 encoded + * realm, 0 if unspecified. + */ + QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_REALM = 4, + /* An array of 16 x unsigned 32-bit value; roaming consortium ids to + * match, 0 if unspecified. + */ + QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_CNSRTM_ID = 5, + /* An array of 6 x unsigned 8-bit value; MCC/MNC combination, 0s if + * unspecified. + */ + QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_PLMN = 6, + + /* Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_PNO_SET_LIST sub command. + */ + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS = 7, + /* Array of nested + * QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_* + * attributes. Array size = + * QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS. + */ + QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORKS_LIST = 8, + /* An array of 33 x unsigned 8-bit value; NULL terminated SSID */ + QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID = 9, + /* Signed 8-bit value; threshold for considering this SSID as found, + * required granularity for this threshold is 4 dBm to 8 dBm. + */ + QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_RSSI_THRESHOLD + = 10, + /* Unsigned 8-bit value; WIFI_PNO_FLAG_XXX */ + QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS = 11, + /* Unsigned 8-bit value; auth bit field for matching WPA IE */ + QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT = 12, + /* Unsigned 8-bit to indicate ePNO type; + * It takes values from qca_wlan_epno_type + */ + QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_TYPE = 13, + + /* Nested attribute to send the channel list */ + QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_CHANNEL_LIST = 14, + + /* Unsigned 32-bit value; indicates the interval between PNO scan + * cycles in msec. + */ + QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_SCAN_INTERVAL = 15, + QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI = 16, + QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI = 17, + QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX = 18, + QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS = 19, + QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS = 20, + QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS = 21, + QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS = 22, + /* Unsigned 32-bit value, representing the PNO Request ID */ + QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID = 23, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_PNO_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_PNO_MAX = + QCA_WLAN_VENDOR_ATTR_PNO_AFTER_LAST - 1, +}; + +/** + * qca_wlan_vendor_acs_select_reason: This represents the different reasons why + * the ACS has to be triggered. These values are used by + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON and + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON + */ +enum qca_wlan_vendor_acs_select_reason { + /* Represents the reason that the ACS triggered during the AP start */ + QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT, + /* Represents the reason that DFS found with the current channel */ + QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS, + /* Represents the reason that LTE co-exist in the current band. */ + QCA_WLAN_VENDOR_ACS_SELECT_REASON_LTE_COEX, +}; + +/** + * qca_wlan_vendor_attr_external_acs_policy: Attribute values for + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY to the vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This represents the + * external ACS policies to select the channels w.r.t. the PCL weights. + * (QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL represents the channels and + * their PCL weights.) + * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY: Mandatory to + * select a channel with non-zero PCL weight. + * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED: Prefer a + * channel with non-zero PCL weight. + * + */ +enum qca_wlan_vendor_attr_external_acs_policy { + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY, +}; + +/** + * qca_wlan_vendor_channel_prop_flags: This represent the flags for a channel. + * This is used by QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS. + */ +enum qca_wlan_vendor_channel_prop_flags { + /* Bits 0, 1, 2, and 3 are reserved */ + + /* Turbo channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_TURBO = 1 << 4, + /* CCK channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_CCK = 1 << 5, + /* OFDM channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_OFDM = 1 << 6, + /* 2.4 GHz spectrum channel. */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_2GHZ = 1 << 7, + /* 5 GHz spectrum channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_5GHZ = 1 << 8, + /* Only passive scan allowed */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_PASSIVE = 1 << 9, + /* Dynamic CCK-OFDM channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_DYN = 1 << 10, + /* GFSK channel (FHSS PHY) */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_GFSK = 1 << 11, + /* Radar found on channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_RADAR = 1 << 12, + /* 11a static turbo channel only */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_STURBO = 1 << 13, + /* Half rate channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HALF = 1 << 14, + /* Quarter rate channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_QUARTER = 1 << 15, + /* HT 20 channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT20 = 1 << 16, + /* HT 40 with extension channel above */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40PLUS = 1 << 17, + /* HT 40 with extension channel below */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40MINUS = 1 << 18, + /* HT 40 intolerant */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40INTOL = 1 << 19, + /* VHT 20 channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT20 = 1 << 20, + /* VHT 40 with extension channel above */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT40PLUS = 1 << 21, + /* VHT 40 with extension channel below */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT40MINUS = 1 << 22, + /* VHT 80 channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT80 = 1 << 23, + /* HT 40 intolerant mark bit for ACS use */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40INTOLMARK = 1 << 24, + /* Channel temporarily blocked due to noise */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_BLOCKED = 1 << 25, + /* VHT 160 channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT160 = 1 << 26, + /* VHT 80+80 channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT80_80 = 1 << 27, + /* HE 20 channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE20 = 1 << 28, + /* HE 40 with extension channel above */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40PLUS = 1 << 29, + /* HE 40 with extension channel below */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40MINUS = 1 << 30, + /* HE 40 intolerant */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOL = 1 << 31, +}; + +/** + * qca_wlan_vendor_channel_prop_flags_2: This represents the flags for a + * channel, and is a continuation of qca_wlan_vendor_channel_prop_flags. This is + * used by QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2. + */ +enum qca_wlan_vendor_channel_prop_flags_2 { + /* HE 40 intolerant mark bit for ACS use */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOLMARK = 1 << 0, + /* HE 80 channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE80 = 1 << 1, + /* HE 160 channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE160 = 1 << 2, + /* HE 80+80 channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE80_80 = 1 << 3, +}; + +/** + * qca_wlan_vendor_channel_prop_flags_ext: This represent the extended flags for + * each channel. This is used by + * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT. + */ +enum qca_wlan_vendor_channel_prop_flags_ext { + /* Radar found on channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_RADAR_FOUND = 1 << 0, + /* DFS required on channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS = 1 << 1, + /* DFS required on channel for 2nd band of 80+80 */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS_CFREQ2 = 1 << 2, + /* If channel has been checked for DFS */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS_CLEAR = 1 << 3, + /* Excluded in 802.11d */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_11D_EXCLUDED = 1 << 4, + /* Channel Switch Announcement received on this channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_CSA_RECEIVED = 1 << 5, + /* Ad-hoc is not allowed */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_ADHOC = 1 << 6, + /* Station only channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_HOSTAP = 1 << 7, + /* DFS radar history for slave device (STA mode) */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_HISTORY_RADAR = 1 << 8, + /* DFS CAC valid for slave device (STA mode) */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_CAC_VALID = 1 << 9, +}; + +/** + * qca_wlan_vendor_external_acs_event_chan_info_attr: Represents per channel + * information. These attributes are sent as part of + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO. Each set of the following + * attributes correspond to a single channel. + */ +enum qca_wlan_vendor_external_acs_event_chan_info_attr { + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_INVALID = 0, + + /* A bitmask (u32) with flags specified in + * enum qca_wlan_vendor_channel_prop_flags. + */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS = 1, + /* A bitmask (u32) with flags specified in + * enum qca_wlan_vendor_channel_prop_flags_ext. + */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT = 2, + /* frequency in MHz (u32) */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ = 3, + /* maximum regulatory transmission power (u32) */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_REG_POWER = 4, + /* maximum transmission power (u32) */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_POWER = 5, + /* minimum transmission power (u32) */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MIN_POWER = 6, + /* regulatory class id (u8) */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_REG_CLASS_ID = 7, + /* maximum antenna gain in (u8) */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_ANTENNA_GAIN = 8, + /* VHT segment 0 (u8) */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0 = 9, + /* VHT segment 1 (u8) */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1 = 10, + /* A bitmask (u32) with flags specified in + * enum qca_wlan_vendor_channel_prop_flags_2. + */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2 = 11, + + /* keep last */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST, + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX = + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST - 1, +}; + +/** + * qca_wlan_vendor_attr_pcl: Represents attributes for + * preferred channel list (PCL). These attributes are sent as part of + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL and + * QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST. + */ +enum qca_wlan_vendor_attr_pcl { + QCA_WLAN_VENDOR_ATTR_PCL_INVALID = 0, + + /* Channel number (u8) */ + QCA_WLAN_VENDOR_ATTR_PCL_CHANNEL = 1, + /* Channel weightage (u8) */ + QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT = 2, + /* Channel frequency (u32) in MHz */ + QCA_WLAN_VENDOR_ATTR_PCL_FREQ = 3, + /* Channel flags (u32) + * bit 0 set: channel to be used for GO role, + * bit 1 set: channel to be used on CLI role, + * bit 2 set: channel must be considered for operating channel + * selection & peer chosen operating channel should be + * one of the channels with this flag set, + * bit 3 set: channel should be excluded in GO negotiation + */ + QCA_WLAN_VENDOR_ATTR_PCL_FLAG = 4, +}; + +/** + * qca_wlan_vendor_attr_external_acs_event: Attribute to vendor sub-command + * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This attribute will be sent by + * host driver. + */ +enum qca_wlan_vendor_attr_external_acs_event { + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_INVALID = 0, + + /* This reason (u8) refers to enum qca_wlan_vendor_acs_select_reason. + * This helps ACS module to understand why ACS needs to be started. + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON = 1, + /* Flag attribute to indicate if driver supports spectral scanning */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_SPECTRAL_SUPPORTED = 2, + /* Flag attribute to indicate if 11ac is offloaded to firmware */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_OFFLOAD_ENABLED = 3, + /* Flag attribute to indicate if driver provides additional channel + * capability as part of scan operation + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_ADD_CHAN_STATS_SUPPORT = 4, + /* Flag attribute to indicate interface status is UP */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_AP_UP = 5, + /* Operating mode (u8) of interface. Takes one of enum nl80211_iftype + * values. + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_SAP_MODE = 6, + /* Channel width (u8). It takes one of enum nl80211_chan_width values. + * This is the upper bound of channel width. ACS logic should try to get + * a channel with the specified width and if not found, look for lower + * values. + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_WIDTH = 7, + /* This (u8) will hold values of one of enum nl80211_bands */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_BAND = 8, + /* PHY/HW mode (u8). Takes one of enum qca_wlan_vendor_acs_hw_mode + * values + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PHY_MODE = 9, + /* Array of (u32) supported frequency list among which ACS should choose + * best frequency. + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_FREQ_LIST = 10, + /* Preferred channel list by the driver which will have array of nested + * values as per enum qca_wlan_vendor_attr_pcl attribute. + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL = 11, + /* Array of nested attribute for each channel. It takes attr as defined + * in enum qca_wlan_vendor_external_acs_event_chan_info_attr. + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO = 12, + /* External ACS policy such as PCL mandatory, PCL preferred, etc. + * It uses values defined in enum + * qca_wlan_vendor_attr_external_acs_policy. + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY = 13, + /* Reference RF Operating Parameter (RROP) availability information + * (u16). It uses values defined in enum + * qca_wlan_vendor_attr_rropavail_info. + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_RROPAVAIL_INFO = 14, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_MAX = + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST - 1, +}; + +/** + * qca_wlan_vendor_attr_external_acs_channels: Attributes to vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This carries a list of channels + * in priority order as decided after ACS operation in userspace. + */ +enum qca_wlan_vendor_attr_external_acs_channels { + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_INVALID = 0, + + /* One of reason code (u8) from enum qca_wlan_vendor_acs_select_reason + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON = 1, + + /* Array of nested values for each channel with following attributes: + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_BAND, + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY, + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY, + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0, + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1, + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST = 2, + /* This (u8) will hold values of one of enum nl80211_bands */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_BAND = 3, + /* Primary channel (u8) */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY = 4, + /* Secondary channel (u8) used for HT 40 MHz channels */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY = 5, + /* VHT seg0 channel (u8) */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0 = 6, + /* VHT seg1 channel (u8) */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1 = 7, + /* Channel width (u8). Takes one of enum nl80211_chan_width values. */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH = 8, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_MAX = + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST - 1 +}; + +enum qca_chip_power_save_failure_reason { + /* Indicates if the reason for the failure is due to a protocol + * layer/module. + */ + QCA_CHIP_POWER_SAVE_FAILURE_REASON_PROTOCOL = 0, + /* Indicates if the reason for the failure is due to a hardware issue. + */ + QCA_CHIP_POWER_SAVE_FAILURE_REASON_HARDWARE = 1, +}; + +/** + * qca_attr_chip_power_save_failure: Attributes to vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE. This carries the requisite + * information leading to the power save failure. + */ +enum qca_attr_chip_power_save_failure { + QCA_ATTR_CHIP_POWER_SAVE_FAILURE_INVALID = 0, + /* Reason to cause the power save failure. + * These reasons are represented by + * enum qca_chip_power_save_failure_reason. + */ + QCA_ATTR_CHIP_POWER_SAVE_FAILURE_REASON = 1, + + /* keep last */ + QCA_ATTR_CHIP_POWER_SAVE_FAILURE_LAST, + QCA_ATTR_CHIP_POWER_SAVE_FAILURE_MAX = + QCA_ATTR_CHIP_POWER_SAVE_FAILURE_LAST - 1, +}; + +/** + * qca_wlan_vendor_nud_stats_data_pkt_flags: Flag representing the various + * data types for which the stats have to get collected. + */ +enum qca_wlan_vendor_nud_stats_data_pkt_flags { + QCA_WLAN_VENDOR_NUD_STATS_DATA_ARP = 1 << 0, + QCA_WLAN_VENDOR_NUD_STATS_DATA_DNS = 1 << 1, + QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_HANDSHAKE = 1 << 2, + QCA_WLAN_VENDOR_NUD_STATS_DATA_ICMPV4 = 1 << 3, + QCA_WLAN_VENDOR_NUD_STATS_DATA_ICMPV6 = 1 << 4, + /* Used by QCA_ATTR_NUD_STATS_PKT_TYPE only in nud stats get + * to represent the stats of respective data type. + */ + QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_SYN = 1 << 5, + QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_SYN_ACK = 1 << 6, + QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_ACK = 1 << 7, +}; + +enum qca_wlan_vendor_nud_stats_set_data_pkt_info { + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_INVALID = 0, + /* Represents the data packet type to be monitored (u32). + * Host driver tracks the stats corresponding to each data frame + * represented by these flags. + * These data packets are represented by + * enum qca_wlan_vendor_nud_stats_data_pkt_flags + */ + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_TYPE = 1, + /* Name corresponding to the DNS frame for which the respective DNS + * stats have to get monitored (string). Max string length 255. + */ + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DNS_DOMAIN_NAME = 2, + /* source port on which the respective proto stats have to get + * collected (u32). + */ + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_SRC_PORT = 3, + /* destination port on which the respective proto stats have to get + * collected (u32). + */ + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_PORT = 4, + /* IPv4 address for which the destined data packets have to be + * monitored. (in network byte order), u32. + */ + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_IPV4 = 5, + /* IPv6 address for which the destined data packets have to be + * monitored. (in network byte order), 16 bytes array. + */ + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_IPV6 = 6, + + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_LAST, + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_MAX = + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_LAST - 1, +}; + +/** + * qca_wlan_vendor_attr_nud_stats_set: Attributes to vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET. This carries the requisite + * information to start/stop the NUD statistics collection. + */ +enum qca_attr_nud_stats_set { + QCA_ATTR_NUD_STATS_SET_INVALID = 0, + + /* Flag to start/stop the NUD statistics collection. + * Start - If included, Stop - If not included + */ + QCA_ATTR_NUD_STATS_SET_START = 1, + /* IPv4 address of the default gateway (in network byte order), u32 */ + QCA_ATTR_NUD_STATS_GW_IPV4 = 2, + /* Represents the list of data packet types to be monitored. + * Host driver tracks the stats corresponding to each data frame + * represented by these flags. + * These data packets are represented by + * enum qca_wlan_vendor_nud_stats_set_data_pkt_info + */ + QCA_ATTR_NUD_STATS_SET_DATA_PKT_INFO = 3, + + /* keep last */ + QCA_ATTR_NUD_STATS_SET_LAST, + QCA_ATTR_NUD_STATS_SET_MAX = + QCA_ATTR_NUD_STATS_SET_LAST - 1, +}; + +enum qca_attr_nud_data_stats { + QCA_ATTR_NUD_DATA_STATS_INVALID = 0, + /* Data packet type for which the stats are collected (u32). + * Represented by enum qca_wlan_vendor_nud_stats_data_pkt_flags + */ + QCA_ATTR_NUD_STATS_PKT_TYPE = 1, + /* Name corresponding to the DNS frame for which the respective DNS + * stats are monitored (string). Max string length 255. + */ + QCA_ATTR_NUD_STATS_PKT_DNS_DOMAIN_NAME = 2, + /* source port on which the respective proto stats are collected (u32). + */ + QCA_ATTR_NUD_STATS_PKT_SRC_PORT = 3, + /* destination port on which the respective proto stats are collected + * (u32). + */ + QCA_ATTR_NUD_STATS_PKT_DEST_PORT = 4, + /* IPv4 address for which the destined data packets have to be + * monitored. (in network byte order), u32. + */ + QCA_ATTR_NUD_STATS_PKT_DEST_IPV4 = 5, + /* IPv6 address for which the destined data packets have to be + * monitored. (in network byte order), 16 bytes array. + */ + QCA_ATTR_NUD_STATS_PKT_DEST_IPV6 = 6, + /* Data packet Request count received from netdev (u32). */ + QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_FROM_NETDEV = 7, + /* Data packet Request count sent to lower MAC from upper MAC (u32). */ + QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TO_LOWER_MAC = 8, + /* Data packet Request count received by lower MAC from upper MAC + * (u32) + */ + QCA_ATTR_NUD_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC = 9, + /* Data packet Request count successfully transmitted by the device + * (u32) + */ + QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TX_SUCCESS = 10, + /* Data packet Response count received by lower MAC (u32) */ + QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC = 11, + /* Data packet Response count received by upper MAC (u32) */ + QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC = 12, + /* Data packet Response count delivered to netdev (u32) */ + QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_TO_NETDEV = 13, + /* Data Packet Response count that are dropped out of order (u32) */ + QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP = 14, + + /* keep last */ + QCA_ATTR_NUD_DATA_STATS_LAST, + QCA_ATTR_NUD_DATA_STATS_MAX = + QCA_ATTR_NUD_DATA_STATS_LAST - 1, +}; + +/** + * qca_attr_nud_stats_get: Attributes to vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET. This carries the requisite + * NUD statistics collected when queried. + */ +enum qca_attr_nud_stats_get { + QCA_ATTR_NUD_STATS_GET_INVALID = 0, + /* ARP Request count from netdev (u32) */ + QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV = 1, + /* ARP Request count sent to lower MAC from upper MAC (u32) */ + QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC = 2, + /* ARP Request count received by lower MAC from upper MAC (u32) */ + QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC = 3, + /* ARP Request count successfully transmitted by the device (u32) */ + QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS = 4, + /* ARP Response count received by lower MAC (u32) */ + QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC = 5, + /* ARP Response count received by upper MAC (u32) */ + QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC = 6, + /* ARP Response count delivered to netdev (u32) */ + QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV = 7, + /* ARP Response count dropped due to out of order reception (u32) */ + QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP = 8, + /* Flag indicating if the station's link to the AP is active. + * Active Link - If included, Inactive link - If not included + */ + QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE = 9, + /* Flag indicating if there is any duplicate address detected (DAD). + * Yes - If detected, No - If not detected. + */ + QCA_ATTR_NUD_STATS_IS_DAD = 10, + /* List of Data packet types for which the stats are requested. + * This list does not carry ARP stats as they are done by the + * above attributes. Represented by enum qca_attr_nud_data_stats. + */ + QCA_ATTR_NUD_STATS_DATA_PKT_STATS = 11, + + /* keep last */ + QCA_ATTR_NUD_STATS_GET_LAST, + QCA_ATTR_NUD_STATS_GET_MAX = + QCA_ATTR_NUD_STATS_GET_LAST - 1, +}; + +enum qca_wlan_btm_candidate_status { + QCA_STATUS_ACCEPT = 0, + QCA_STATUS_REJECT_EXCESSIVE_FRAME_LOSS_EXPECTED = 1, + QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED = 2, + QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY = 3, + QCA_STATUS_REJECT_LOW_RSSI = 4, + QCA_STATUS_REJECT_HIGH_INTERFERENCE = 5, + QCA_STATUS_REJECT_UNKNOWN = 6, +}; + +enum qca_wlan_vendor_attr_btm_candidate_info { + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_INVALID = 0, + + /* 6-byte MAC address representing the BSSID of transition candidate */ + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID = 1, + /* Unsigned 32-bit value from enum qca_wlan_btm_candidate_status + * returned by the driver. It says whether the BSSID provided in + * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID is acceptable by + * the driver, if not it specifies the reason for rejection. + * Note that the user-space can overwrite the transition reject reason + * codes provided by driver based on more information. + */ + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS = 2, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX = + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_AFTER_LAST - 1, +}; + +enum qca_attr_trace_level { + QCA_ATTR_TRACE_LEVEL_INVALID = 0, + /* + * Nested array of the following attributes: + * QCA_ATTR_TRACE_LEVEL_MODULE, + * QCA_ATTR_TRACE_LEVEL_MASK. + */ + QCA_ATTR_TRACE_LEVEL_PARAM = 1, + /* + * Specific QCA host driver module. Please refer to the QCA host + * driver implementation to get the specific module ID. + */ + QCA_ATTR_TRACE_LEVEL_MODULE = 2, + /* Different trace level masks represented in the QCA host driver. */ + QCA_ATTR_TRACE_LEVEL_MASK = 3, + + /* keep last */ + QCA_ATTR_TRACE_LEVEL_AFTER_LAST, + QCA_ATTR_TRACE_LEVEL_MAX = + QCA_ATTR_TRACE_LEVEL_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_get_he_capabilities - IEEE 802.11ax HE capabilities + */ +enum qca_wlan_vendor_attr_get_he_capabilities { + QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_INVALID = 0, + /* Whether HE capabilities is supported + * (u8 attribute: 0 = not supported, 1 = supported) + */ + QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED = 1, + /* HE PHY capabilities, array of 3 u32 values */ + QCA_WLAN_VENDOR_ATTR_PHY_CAPAB = 2, + /* HE MAC capabilities (u32 attribute) */ + QCA_WLAN_VENDOR_ATTR_MAC_CAPAB = 3, + /* HE MCS map (u32 attribute) */ + QCA_WLAN_VENDOR_ATTR_HE_MCS = 4, + /* Number of SS (u32 attribute) */ + QCA_WLAN_VENDOR_ATTR_NUM_SS = 5, + /* RU count (u32 attribute) */ + QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK = 6, + /* PPE threshold data, array of 8 u32 values */ + QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD = 7, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX = + QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_spectral_scan - Spectral scan config parameters + */ +enum qca_wlan_vendor_attr_spectral_scan { + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_INVALID = 0, + /* Number of times the chip enters spectral scan mode before + * deactivating spectral scans. When set to 0, chip will enter spectral + * scan mode continuously. u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SCAN_COUNT = 1, + /* Spectral scan period. Period increment resolution is 256*Tclk, + * where Tclk = 1/44 MHz (Gmode), 1/40 MHz (Amode). u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SCAN_PERIOD = 2, + /* Spectral scan priority. u32 attribute. */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PRIORITY = 3, + /* Number of FFT data points to compute. u32 attribute. */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_SIZE = 4, + /* Enable targeted gain change before starting the spectral scan FFT. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_GC_ENA = 5, + /* Restart a queued spectral scan. u32 attribute. */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RESTART_ENA = 6, + /* Noise floor reference number for the calculation of bin power. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_NOISE_FLOOR_REF = 7, + /* Disallow spectral scan triggers after TX/RX packets by setting + * this delay value to roughly SIFS time period or greater. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_INIT_DELAY = 8, + /* Number of strong bins (inclusive) per sub-channel, below + * which a signal is declared a narrow band tone. u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_NB_TONE_THR = 9, + /* Specify the threshold over which a bin is declared strong (for + * scan bandwidth analysis). u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_STR_BIN_THR = 10, + /* Spectral scan report mode. u32 attribute. */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_WB_RPT_MODE = 11, + /* RSSI report mode, if the ADC RSSI is below + * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_THR, + * then FFTs will not trigger, but timestamps and summaries get + * reported. u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_RPT_MODE = 12, + /* ADC RSSI must be greater than or equal to this threshold (signed dB) + * to ensure spectral scan reporting with normal error code. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_THR = 13, + /* Format of frequency bin magnitude for spectral scan triggered FFTs: + * 0: linear magnitude, 1: log magnitude (20*log10(lin_mag)). + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PWR_FORMAT = 14, + /* Format of FFT report to software for spectral scan triggered FFTs. + * 0: No FFT report (only spectral scan summary report) + * 1: 2-dword summary of metrics for each completed FFT + spectral scan + * report + * 2: 2-dword summary of metrics for each completed FFT + 1x-oversampled + * bins (in-band) per FFT + spectral scan summary report + * 3: 2-dword summary of metrics for each completed FFT + 2x-oversampled + * bins (all) per FFT + spectral scan summary report + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RPT_MODE = 15, + /* Number of LSBs to shift out in order to scale the FFT bins. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_BIN_SCALE = 16, + /* Set to 1 (with spectral_scan_pwr_format=1), to report bin magnitudes + * in dBm power. u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DBM_ADJ = 17, + /* Per chain enable mask to select input ADC for search FFT. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_CHN_MASK = 18, + /* An unsigned 64-bit integer provided by host driver to identify the + * spectral scan request. This attribute is included in the scan + * response message for @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START + * and used as an attribute in + * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP to identify the + * specific scan to be stopped. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE = 19, + /* Skip interval for FFT reports. u32 attribute */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_PERIOD = 20, + /* Set to report only one set of FFT results. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SHORT_REPORT = 21, + /* Debug level for spectral module in driver. + * 0 : Verbosity level 0 + * 1 : Verbosity level 1 + * 2 : Verbosity level 2 + * 3 : Matched filterID display + * 4 : One time dump of FFT report + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DEBUG_LEVEL = 22, + /* Type of spectral scan request. u32 attribute. + * It uses values defined in enum + * qca_wlan_vendor_attr_spectral_scan_request_type. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE = 23, + + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_MAX = + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_spectral_diag_stats - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS. + */ +enum qca_wlan_vendor_attr_spectral_diag_stats { + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_INVALID = 0, + /* Number of spectral TLV signature mismatches. + * u64 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_SIG_MISMATCH = 1, + /* Number of spectral phyerror events with insufficient length when + * parsing for secondary 80 search FFT report. u64 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_SEC80_SFFT_INSUFFLEN = 2, + /* Number of spectral phyerror events without secondary 80 + * search FFT report. u64 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_NOSEC80_SFFT = 3, + /* Number of spectral phyerror events with vht operation segment 1 id + * mismatches in search fft report. u64 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_VHTSEG1ID_MISMATCH = 4, + /* Number of spectral phyerror events with vht operation segment 2 id + * mismatches in search fft report. u64 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_VHTSEG2ID_MISMATCH = 5, + + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_MAX = + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_spectral_cap - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO. + */ +enum qca_wlan_vendor_attr_spectral_cap { + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_INVALID = 0, + /* Flag attribute to indicate phydiag capability */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_PHYDIAG = 1, + /* Flag attribute to indicate radar detection capability */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_RADAR = 2, + /* Flag attribute to indicate spectral capability */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_SPECTRAL = 3, + /* Flag attribute to indicate advanced spectral capability */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_ADVANCED_SPECTRAL = 4, + /* Spectral hardware generation. u32 attribute. + * It uses values defined in enum + * qca_wlan_vendor_spectral_scan_cap_hw_gen. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN = 5, + + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_MAX = + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_spectral_scan_status - used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS. + */ +enum qca_wlan_vendor_attr_spectral_scan_status { + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_INVALID = 0, + /* Flag attribute to indicate whether spectral scan is enabled */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_IS_ENABLED = 1, + /* Flag attribute to indicate whether spectral scan is in progress*/ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_IS_ACTIVE = 2, + + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_MAX = + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_AFTER_LAST - 1, +}; + +/** + * qca_wlan_vendor_attr_spectral_scan_request_type: Attribute values for + * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE to the vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START. This represents the + * spectral scan request types. + * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN_AND_CONFIG: Request to + * set the spectral parameters and start scan. + * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN: Request to + * only set the spectral parameters. + * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_CONFIG: Request to + * only start the spectral scan. + */ +enum qca_wlan_vendor_attr_spectral_scan_request_type { + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN_AND_CONFIG, + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN, + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_CONFIG, +}; + +/** + * qca_wlan_vendor_spectral_scan_cap_hw_gen: Attribute values for + * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN to the vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO. This represents the + * spectral hardware generation. + * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_1: generation 1 + * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_2: generation 2 + * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_3: generation 3 + */ +enum qca_wlan_vendor_spectral_scan_cap_hw_gen { + QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_1 = 0, + QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_2 = 1, + QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_3 = 2, +}; + +enum qca_wlan_vendor_tos { + QCA_WLAN_VENDOR_TOS_BK = 0, + QCA_WLAN_VENDOR_TOS_BE = 1, + QCA_WLAN_VENDOR_TOS_VI = 2, + QCA_WLAN_VENDOR_TOS_VO = 3, +}; + +/** + * enum qca_wlan_vendor_attr_active_tos - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS. + */ +enum qca_wlan_vendor_attr_active_tos { + QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_INVALID = 0, + /* Type Of Service - Represented by qca_wlan_vendor_tos */ + QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS = 1, + /* Flag attribute representing the start (attribute included) or stop + * (attribute not included) of the respective TOS. + */ + QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_START = 2, +}; + +enum qca_wlan_vendor_hang_reason { + /* Unspecified reason */ + QCA_WLAN_HANG_REASON_UNSPECIFIED = 0, + /* No Map for the MAC entry for the received frame */ + QCA_WLAN_HANG_RX_HASH_NO_ENTRY_FOUND = 1, + /* Peer deletion timeout happened */ + QCA_WLAN_HANG_PEER_DELETION_TIMEDOUT = 2, + /* Peer unmap timeout */ + QCA_WLAN_HANG_PEER_UNMAP_TIMEDOUT = 3, + /* Scan request timed out */ + QCA_WLAN_HANG_SCAN_REQ_EXPIRED = 4, + /* Consecutive Scan attempt failures */ + QCA_WLAN_HANG_SCAN_ATTEMPT_FAILURES = 5, + /* Unable to get the message buffer */ + QCA_WLAN_HANG_GET_MSG_BUFF_FAILURE = 6, + /* Current command processing is timedout */ + QCA_WLAN_HANG_ACTIVE_LIST_TIMEOUT = 7, + /* Timeout for an ACK from FW for suspend request */ + QCA_WLAN_HANG_SUSPEND_TIMEOUT = 8, + /* Timeout for an ACK from FW for resume request */ + QCA_WLAN_HANG_RESUME_TIMEOUT = 9, + /* Transmission timeout for consecutive data frames */ + QCA_WLAN_HANG_TRANSMISSIONS_TIMEOUT = 10, + /* Timeout for the TX completion status of data frame */ + QCA_WLAN_HANG_TX_COMPLETE_TIMEOUT = 11, + /* DXE failure for TX/RX, DXE resource unavailability */ + QCA_WLAN_HANG_DXE_FAILURE = 12, + /* WMI pending commands exceed the maximum count */ + QCA_WLAN_HANG_WMI_EXCEED_MAX_PENDING_CMDS = 13, +}; + +/** + * enum qca_wlan_vendor_attr_hang - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_HANG. + */ +enum qca_wlan_vendor_attr_hang { + QCA_WLAN_VENDOR_ATTR_HANG_INVALID = 0, + /* Reason for the hang - u32 attribute with a value from enum + * qca_wlan_vendor_hang_reason. + */ + QCA_WLAN_VENDOR_ATTR_HANG_REASON = 1, + + QCA_WLAN_VENDOR_ATTR_HANG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_HANG_MAX = + QCA_WLAN_VENDOR_ATTR_HANG_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_flush_pending - Attributes for + * flushing pending traffic in firmware. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_ADDR: Configure peer MAC address. + * @QCA_WLAN_VENDOR_ATTR_AC: Configure access category of the pending + * packets. It is u8 value with bit 0~3 represent AC_BE, AC_BK, + * AC_VI, AC_VO respectively. Set the corresponding bit to 1 to + * flush packets with access category. + */ +enum qca_wlan_vendor_attr_flush_pending { + QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_PEER_ADDR = 1, + QCA_WLAN_VENDOR_ATTR_AC = 2, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_MAX = + QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_rropavail_info - Specifies whether Representative + * RF Operating Parameter (RROP) information is available, and if so, at which + * point in the application-driver interaction sequence it can be retrieved by + * the application from the driver. This point may vary by architecture and + * other factors. This is a u16 value. + */ +enum qca_wlan_vendor_attr_rropavail_info { + /* RROP information is unavailable. */ + QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_UNAVAILABLE, + /* RROP information is available and the application can retrieve the + * information after receiving an QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS + * event from the driver. + */ + QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_EXTERNAL_ACS_START, + /* RROP information is available only after a vendor specific scan + * (requested using QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN) has + * successfully completed. The application can retrieve the information + * after receiving the QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE event from + * the driver. + */ + QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_VSCAN_END, +}; + +/** + * enum qca_wlan_vendor_attr_rrop_info - Specifies vendor specific + * Representative RF Operating Parameter (RROP) information. It is sent for the + * vendor command QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO. This information is + * intended for use by external Auto Channel Selection applications. It provides + * guidance values for some RF parameters that are used by the system during + * operation. These values could vary by channel, band, radio, and so on. + */ +enum qca_wlan_vendor_attr_rrop_info { + QCA_WLAN_VENDOR_ATTR_RROP_INFO_INVALID = 0, + + /* Representative Tx Power List (RTPL) which has an array of nested + * values as per attributes in enum qca_wlan_vendor_attr_rtplinst. + */ + QCA_WLAN_VENDOR_ATTR_RROP_INFO_RTPL = 1, + + QCA_WLAN_VENDOR_ATTR_RROP_INFO_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_RROP_INFO_MAX = + QCA_WLAN_VENDOR_ATTR_RROP_INFO_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_rtplinst - Specifies attributes for individual list + * entry instances in the Representative Tx Power List (RTPL). It provides + * simplified power values intended for helping external Auto channel Selection + * applications compare potential Tx power performance between channels, other + * operating conditions remaining identical. These values are not necessarily + * the actual Tx power values that will be used by the system. They are also not + * necessarily the max or average values that will be used. Instead, they are + * relative, summarized keys for algorithmic use computed by the driver or + * underlying firmware considering a number of vendor specific factors. + */ +enum qca_wlan_vendor_attr_rtplinst { + QCA_WLAN_VENDOR_ATTR_RTPLINST_INVALID = 0, + + /* Primary channel number (u8) */ + QCA_WLAN_VENDOR_ATTR_RTPLINST_PRIMARY = 1, + /* Representative Tx power in dBm (s32) with emphasis on throughput. */ + QCA_WLAN_VENDOR_ATTR_RTPLINST_TXPOWER_THROUGHPUT = 2, + /* Representative Tx power in dBm (s32) with emphasis on range. */ + QCA_WLAN_VENDOR_ATTR_RTPLINST_TXPOWER_RANGE = 3, + + QCA_WLAN_VENDOR_ATTR_RTPLINST_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_RTPLINST_MAX = + QCA_WLAN_VENDOR_ATTR_RTPLINST_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_config_latency_level - Level for + * wlan latency module. + * + * There will be various of Wi-Fi functionality like scan/roaming/adaptive + * power saving which would causing data exchange out of service, this + * would be a big impact on latency. For latency sensitive applications over + * Wi-Fi are intolerant to such operations and thus would configure them + * to meet their respective needs. It is well understood by such applications + * that altering the default behavior would degrade the Wi-Fi functionality + * w.r.t the above pointed WLAN operations. + * + * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL: + * Default WLAN operation level which throughput orientated. + * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MODERATE: + * Use moderate level to improve latency by limit scan duration. + * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_LOW: + * Use low latency level to benifit application like concurrent + * downloading or video streaming via constraint scan/adaptive PS. + * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW: + * Use ultra low latency level to benefit for gaming/voice + * application via constraint scan/roaming/adaptive PS. + */ +enum qca_wlan_vendor_attr_config_latency_level { + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL = 1, + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MODERATE = 2, + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_LOW = 3, + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW = 4, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MAX = + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_wlan_mac - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO. + */ +enum qca_wlan_vendor_attr_mac { + QCA_WLAN_VENDOR_ATTR_MAC_INVALID = 0, + + /* MAC mode info list which has an array of nested values as + * per attributes in enum qca_wlan_vendor_attr_mac_mode_info. + */ + QCA_WLAN_VENDOR_ATTR_MAC_INFO = 1, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_MAC_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_MAC_MAX = + QCA_WLAN_VENDOR_ATTR_MAC_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_mac_iface_info - Information of the connected + * Wi-Fi netdev interface on a respective MAC. + * Used by the attribute QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO. + */ +enum qca_wlan_vendor_attr_mac_iface_info { + QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_INVALID = 0, + /* Wi-Fi netdev's interface index (u32) */ + QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_IFINDEX = 1, + /* Associated frequency in MHz of the connected Wi-Fi interface (u32) */ + QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_FREQ = 2, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_MAX = + QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_mac_info - Points to MAC the information. + * Used by the attribute QCA_WLAN_VENDOR_ATTR_MAC_INFO of the + * vendor command QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO. + */ +enum qca_wlan_vendor_attr_mac_info { + QCA_WLAN_VENDOR_ATTR_MAC_INFO_INVALID = 0, + /* Hardware MAC ID associated for the MAC (u32) */ + QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAC_ID = 1, + /* Band supported by the MAC at a given point. + * This is a u32 bitmask of BIT(NL80211_BAND_*) as described in %enum + * nl80211_band. + */ + QCA_WLAN_VENDOR_ATTR_MAC_INFO_BAND = 2, + /* Refers to list of WLAN netdev interfaces associated with this MAC. + * Represented by enum qca_wlan_vendor_attr_mac_iface_info. + */ + QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO = 3, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_MAC_INFO_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAX = + QCA_WLAN_VENDOR_ATTR_MAC_INFO_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_get_logger_features - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET. + */ +enum qca_wlan_vendor_attr_get_logger_features { + QCA_WLAN_VENDOR_ATTR_LOGGER_INVALID = 0, + /* Unsigned 32-bit enum value of wifi_logger_supported_features */ + QCA_WLAN_VENDOR_ATTR_LOGGER_SUPPORTED = 1, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_LOGGER_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_LOGGER_MAX = + QCA_WLAN_VENDOR_ATTR_LOGGER_AFTER_LAST - 1, +}; + +/** + * enum wifi_logger_supported_features - Values for supported logger features + */ +enum wifi_logger_supported_features { + WIFI_LOGGER_MEMORY_DUMP_FEATURE = (1 << (0)), + WIFI_LOGGER_PER_PACKET_TX_RX_STATUS_FEATURE = (1 << (1)), + WIFI_LOGGER_CONNECT_EVENT_FEATURE = (1 << (2)), + WIFI_LOGGER_POWER_EVENT_FEATURE = (1 << (3)), + WIFI_LOGGER_WAKE_LOCK_FEATURE = (1 << (4)), + WIFI_LOGGER_VERBOSE_FEATURE = (1 << (5)), + WIFI_LOGGER_WATCHDOG_TIMER_FEATURE = (1 << (6)), + WIFI_LOGGER_DRIVER_DUMP_FEATURE = (1 << (7)), + WIFI_LOGGER_PACKET_FATE_FEATURE = (1 << (8)), +}; + +/** + * enum qca_wlan_tdls_caps_features_supported - Values for TDLS get + * capabilities features + */ +enum qca_wlan_tdls_caps_features_supported { + WIFI_TDLS_SUPPORT = (1 << (0)), + WIFI_TDLS_EXTERNAL_CONTROL_SUPPORT = (1 << (1)), + WIFI_TDLS_OFFCHANNEL_SUPPORT = (1 << (2)) +}; + +/** + * enum qca_wlan_vendor_attr_tdls_get_capabilities - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES. + */ +enum qca_wlan_vendor_attr_tdls_get_capabilities { + QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_INVALID = 0, + /* Indicates the max concurrent sessions */ + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX_CONC_SESSIONS, + /* Indicates the support for features */ + /* Unsigned 32-bit bitmap qca_wlan_tdls_caps_features_supported + */ + QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_FEATURES_SUPPORTED, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX = + QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_offloaded_packets_sending_control - Offload packets control + * command used as value for the attribute + * QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL. + */ +enum qca_wlan_offloaded_packets_sending_control { + QCA_WLAN_OFFLOADED_PACKETS_SENDING_CONTROL_INVALID = 0, + QCA_WLAN_OFFLOADED_PACKETS_SENDING_START, + QCA_WLAN_OFFLOADED_PACKETS_SENDING_STOP +}; + +/** + * enum qca_wlan_vendor_attr_offloaded_packets - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS. + */ +enum qca_wlan_vendor_attr_offloaded_packets { + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_INVALID = 0, + /* Takes valid value from the enum + * qca_wlan_offloaded_packets_sending_control + * Unsigned 32-bit value + */ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_REQUEST_ID, + /* array of u8 len: Max packet size */ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_IP_PACKET_DATA, + /* 6-byte MAC address used to represent source MAC address */ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SRC_MAC_ADDR, + /* 6-byte MAC address used to represent destination MAC address */ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR, + /* Unsigned 32-bit value, in milli seconds */ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_MAX = + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_rssi_monitoring_control - RSSI control commands used as values + * by the attribute QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL. + */ +enum qca_wlan_rssi_monitoring_control { + QCA_WLAN_RSSI_MONITORING_CONTROL_INVALID = 0, + QCA_WLAN_RSSI_MONITORING_START, + QCA_WLAN_RSSI_MONITORING_STOP, +}; + +/** + * enum qca_wlan_vendor_attr_rssi_monitoring - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI. + */ +enum qca_wlan_vendor_attr_rssi_monitoring { + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_INVALID = 0, + /* Takes valid value from the enum + * qca_wlan_rssi_monitoring_control + * Unsigned 32-bit value enum qca_wlan_rssi_monitoring_control + */ + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID, + /* Signed 8-bit value in dBm */ + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX_RSSI, + /* Signed 8-bit value in dBm */ + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MIN_RSSI, + /* attributes to be used/received in callback */ + /* 6-byte MAC address used to represent current BSSID MAC address */ + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_BSSID, + /* Signed 8-bit value indicating the current RSSI */ + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_RSSI, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX = + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_ndp_params - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_NDP. + */ +enum qca_wlan_vendor_attr_ndp_params { + QCA_WLAN_VENDOR_ATTR_NDP_PARAM_INVALID = 0, + /* Unsigned 32-bit value + * enum of sub commands values in qca_wlan_ndp_sub_cmd + */ + QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD, + /* Unsigned 16-bit value */ + QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID, + /* NL attributes for data used NDP SUB cmds */ + /* Unsigned 32-bit value indicating a service info */ + QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID, + /* Unsigned 32-bit value; channel frequency in MHz */ + QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL, + /* Interface Discovery MAC address. An array of 6 Unsigned int8 */ + QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR, + /* Interface name on which NDP is being created */ + QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR, + /* Unsigned 32-bit value for security */ + /* CONFIG_SECURITY is deprecated, use NCS_SK_TYPE/PMK/SCID instead */ + QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_SECURITY, + /* Unsigned 32-bit value for QoS */ + QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS, + /* Array of u8: len = QCA_WLAN_VENDOR_ATTR_NAN_DP_APP_INFO_LEN */ + QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO, + /* Unsigned 32-bit value for NDP instance Id */ + QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID, + /* Array of instance Ids */ + QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY, + /* Unsigned 32-bit value for initiator/responder NDP response code + * accept/reject + */ + QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE, + /* NDI MAC address. An array of 6 Unsigned int8 */ + QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR, + /* Unsigned 32-bit value errors types returned by driver + * The wifi_nan.h in AOSP project platform/hardware/libhardware_legacy + * NanStatusType includes these values. + */ + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE, + /* Unsigned 32-bit value error values returned by driver + * The nan_i.h in AOSP project platform/hardware/qcom/wlan + * NanInternalStatusType includes these values. + */ + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE, + /* Unsigned 32-bit value for Channel setup configuration + * The wifi_nan.h in AOSP project platform/hardware/libhardware_legacy + * NanDataPathChannelCfg includes these values. + */ + QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_CONFIG, + /* Unsigned 32-bit value for Cipher Suite Shared Key Type */ + QCA_WLAN_VENDOR_ATTR_NDP_CSID, + /* Array of u8: len = NAN_PMK_INFO_LEN 32 bytes */ + QCA_WLAN_VENDOR_ATTR_NDP_PMK, + /* Security Context Identifier that contains the PMKID + * Array of u8: len = NAN_SCID_BUF_LEN 1024 bytes + */ + QCA_WLAN_VENDOR_ATTR_NDP_SCID, + /* Array of u8: len = NAN_SECURITY_MAX_PASSPHRASE_LEN 63 bytes */ + QCA_WLAN_VENDOR_ATTR_NDP_PASSPHRASE, + /* Array of u8: len = NAN_MAX_SERVICE_NAME_LEN 255 bytes */ + QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_NAME, + /* Unsigned 32-bit bitmap indicating schedule update + * BIT_0: NSS Update + * BIT_1: Channel list update + */ + QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_REASON, + /* Unsigned 32-bit value for NSS */ + QCA_WLAN_VENDOR_ATTR_NDP_NSS, + /* Unsigned 32-bit value for NUMBER NDP CHANNEL */ + QCA_WLAN_VENDOR_ATTR_NDP_NUM_CHANNELS, + /* Unsigned 32-bit value for CHANNEL BANDWIDTH + * 0:20 MHz, 1:40 MHz, 2:80 MHz, 3:160 MHz + */ + QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_WIDTH, + /* Array of channel/band width */ + QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_INFO, + /* IPv6 address used by NDP (in network byte order), 16 bytes array. + * This attribute is used and optional for ndp request, ndp response, + * ndp indication, and ndp confirm. + */ + QCA_WLAN_VENDOR_ATTR_NDP_IPV6_ADDR = 27, + /* Unsigned 16-bit value indicating transport port used by NDP. + * This attribute is used and optional for ndp response, ndp indication, + * and ndp confirm. + */ + QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PORT = 28, + /* Unsigned 8-bit value indicating protocol used by NDP and assigned by + * the Internet Assigned Numbers Authority (IANA) as per: + * https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml + * This attribute is used and optional for ndp response, ndp indication, + * and ndp confirm. + */ + QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PROTOCOL = 29, + /* Unsigned 8-bit value indicating if NDP remote peer supports NAN NDPE. + * 1:support 0:not support + */ + QCA_WLAN_VENDOR_ATTR_PEER_NDPE_SUPPORT = 30, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_MAX = + QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST - 1, +}; + +enum qca_wlan_ndp_sub_cmd { + QCA_WLAN_VENDOR_ATTR_NDP_INVALID = 0, + /* Command to create a NAN data path interface */ + QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_CREATE = 1, + /* Command to delete a NAN data path interface */ + QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_DELETE = 2, + /* Command to initiate a NAN data path session */ + QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_REQUEST = 3, + /* Command to notify if the NAN data path session was sent */ + QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_RESPONSE = 4, + /* Command to respond to NAN data path session */ + QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_REQUEST = 5, + /* Command to notify on the responder about the response */ + QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_RESPONSE = 6, + /* Command to initiate a NAN data path end */ + QCA_WLAN_VENDOR_ATTR_NDP_END_REQUEST = 7, + /* Command to notify the if end request was sent */ + QCA_WLAN_VENDOR_ATTR_NDP_END_RESPONSE = 8, + /* Command to notify the peer about the end request */ + QCA_WLAN_VENDOR_ATTR_NDP_REQUEST_IND = 9, + /* Command to confirm the NAN data path session is complete */ + QCA_WLAN_VENDOR_ATTR_NDP_CONFIRM_IND = 10, + /* Command to indicate the peer about the end request being received */ + QCA_WLAN_VENDOR_ATTR_NDP_END_IND = 11, + /* Command to indicate the peer of schedule update */ + QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_IND = 12 +}; + +/** + * enum qca_wlan_vendor_attr_nd_offload - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD. + */ +enum qca_wlan_vendor_attr_nd_offload { + QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_INVALID = 0, + /* Flag to set Neighbour Discovery offload */ + QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG, + /* Keep last */ + QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX = + QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_AFTER_LAST - 1, +}; + +/** + * enum packet_filter_sub_cmd - Packet filter sub commands + */ +enum packet_filter_sub_cmd { + /** + * Write packet filter program and/or data. The driver/firmware should + * disable APF before writing into local buffer and re-enable APF after + * writing is done. + */ + QCA_WLAN_SET_PACKET_FILTER = 1, + /* Get packet filter feature capabilities from driver */ + QCA_WLAN_GET_PACKET_FILTER = 2, + /** + * Write packet filter program and/or data. User space will send the + * %QCA_WLAN_DISABLE_PACKET_FILTER command before issuing this command + * and will send the %QCA_WLAN_ENABLE_PACKET_FILTER afterwards. The key + * difference from that %QCA_WLAN_SET_PACKET_FILTER is the control over + * enable/disable is given to user space with this command. Also, + * user space sends the length of program portion in the buffer within + * %QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH. + */ + QCA_WLAN_WRITE_PACKET_FILTER = 3, + /* Read packet filter program and/or data */ + QCA_WLAN_READ_PACKET_FILTER = 4, + /* Enable APF feature */ + QCA_WLAN_ENABLE_PACKET_FILTER = 5, + /* Disable APF feature */ + QCA_WLAN_DISABLE_PACKET_FILTER = 6, +}; + +/** + * enum qca_wlan_vendor_attr_packet_filter - BPF control commands used by + * vendor QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER. + */ +enum qca_wlan_vendor_attr_packet_filter { + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID = 0, + /* Unsigned 32-bit enum passed using packet_filter_sub_cmd */ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SUB_CMD, + /* Unsigned 32-bit value indicating the packet filter version */ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION, + /* Unsigned 32-bit value indicating the packet filter id */ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_ID, + /** + * Unsigned 32-bit value indicating the packet filter size including + * program + data. + */ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE, + /* Unsigned 32-bit value indicating the packet filter current offset */ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET, + /* Program and/or data in bytes */ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM, + /* Unsigned 32-bit value of the length of the program section in packet + * filter buffer. + */ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH = 7, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX = + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_drv_info - WLAN driver info used by vendor command + * QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE. + */ +enum qca_wlan_vendor_drv_info { + QCA_WLAN_VENDOR_ATTR_DRV_INFO_INVALID = 0, + /* Maximum Message size info between firmware & HOST + * Unsigned 32-bit value + */ + QCA_WLAN_VENDOR_ATTR_DRV_INFO_BUS_SIZE, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_DRV_INFO_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_DRV_INFO_MAX = + QCA_WLAN_VENDOR_ATTR_DRV_INFO_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_wake_stats - Wake lock stats used by vendor + * command QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS. + */ +enum qca_wlan_vendor_attr_wake_stats { + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_INVALID = 0, + /* Unsigned 32-bit value indicating the total count of wake event */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_CMD_EVENT_WAKE, + /* Array of individual wake count, each index representing wake reason + */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_CMD_EVENT_WAKE_CNT_PTR, + /* Unsigned 32-bit value representing wake count array */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_CMD_EVENT_WAKE_CNT_SZ, + /* Unsigned 32-bit total wake count value of driver/fw */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_DRIVER_FW_LOCAL_WAKE, + /* Array of wake stats of driver/fw */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_PTR, + /* Unsigned 32-bit total wake count value of driver/fw */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_SZ, + /* Unsigned 32-bit total wake count value of packets received */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_RX_DATA_WAKE, + /* Unsigned 32-bit wake count value unicast packets received */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_UNICAST_CNT, + /* Unsigned 32-bit wake count value multicast packets received */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_MULTICAST_CNT, + /* Unsigned 32-bit wake count value broadcast packets received */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_BROADCAST_CNT, + /* Unsigned 32-bit wake count value of ICMP packets */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP_PKT, + /* Unsigned 32-bit wake count value of ICMP6 packets */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_PKT, + /* Unsigned 32-bit value ICMP6 router advertisement */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_RA, + /* Unsigned 32-bit value ICMP6 neighbor advertisement */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_NA, + /* Unsigned 32-bit value ICMP6 neighbor solicitation */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_NS, + /* Unsigned 32-bit wake count value of receive side ICMP4 multicast */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP4_RX_MULTICAST_CNT, + /* Unsigned 32-bit wake count value of receive side ICMP6 multicast */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_RX_MULTICAST_CNT, + /* Unsigned 32-bit wake count value of receive side multicast */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_OTHER_RX_MULTICAST_CNT, + /* Unsigned 32-bit wake count value of a given RSSI breach */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RSSI_BREACH_CNT, + /* Unsigned 32-bit wake count value of low RSSI */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_LOW_RSSI_CNT, + /* Unsigned 32-bit value GSCAN count */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_GSCAN_CNT, + /* Unsigned 32-bit value PNO complete count */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_PNO_COMPLETE_CNT, + /* Unsigned 32-bit value PNO match count */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_PNO_MATCH_CNT, + /* keep last */ + QCA_WLAN_VENDOR_GET_WAKE_STATS_AFTER_LAST, + QCA_WLAN_VENDOR_GET_WAKE_STATS_MAX = + QCA_WLAN_VENDOR_GET_WAKE_STATS_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_thermal_cmd - Vendor subcmd attributes to set + * cmd value. Used for NL attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command. + */ +enum qca_wlan_vendor_attr_thermal_cmd { + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_INVALID = 0, + /* The value of command, driver will implement different operations + * according to this value. It uses values defined in + * enum qca_wlan_vendor_attr_thermal_cmd_type. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE = 1, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_MAX = + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_AFTER_LAST - 1 +}; + +/** + * qca_wlan_vendor_attr_thermal_cmd_type: Attribute values for + * QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE to the vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD. This represents the + * thermal command types sent to driver. + * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS: Request to + * get thermal shutdown configuration parameters for display. Parameters + * responded from driver are defined in + * enum qca_wlan_vendor_attr_get_thermal_params_rsp. + * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE: Request to + * get temperature. Host should respond with a temperature data. It is defined + * in enum qca_wlan_vendor_attr_thermal_get_temperature. + * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SUSPEND: Request to execute thermal + * suspend action. + * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_RESUME: Request to execute thermal + * resume action. + */ +enum qca_wlan_vendor_attr_thermal_cmd_type { + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS, + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE, + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SUSPEND, + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_RESUME, +}; + +/** + * enum qca_wlan_vendor_attr_thermal_get_temperature - vendor subcmd attributes + * to get chip temperature by user. + * enum values are used for NL attributes for data used by + * QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE command for data used + * by QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command. + */ +enum qca_wlan_vendor_attr_thermal_get_temperature { + QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_INVALID = 0, + /* Temperature value (degree Celsius) from driver. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_DATA, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_MAX = + QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_get_thermal_params_rsp - vendor subcmd attributes + * to get configuration parameters of thermal shutdown feature. Enum values are + * used by QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS command for data + * used by QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command. + */ +enum qca_wlan_vendor_attr_get_thermal_params_rsp { + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_INVALID = 0, + /* Indicate if the thermal shutdown feature is enabled. + * NLA_FLAG attribute. + */ + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SHUTDOWN_EN, + /* Indicate if the auto mode is enabled. + * Enable: Driver triggers the suspend/resume action. + * Disable: User space triggers the suspend/resume action. + * NLA_FLAG attribute. + */ + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SHUTDOWN_AUTO_EN, + /* Thermal resume threshold (degree Celsius). Issue the resume command + * if the temperature value is lower than this threshold. + * u16 attribute. + */ + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_RESUME_THRESH, + /* Thermal warning threshold (degree Celsius). FW reports temperature + * to driver if it's higher than this threshold. + * u16 attribute. + */ + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_WARNING_THRESH, + /* Thermal suspend threshold (degree Celsius). Issue the suspend command + * if the temperature value is higher than this threshold. + * u16 attribute. + */ + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SUSPEND_THRESH, + /* FW reports temperature data periodically at this interval (ms). + * u16 attribute. + */ + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SAMPLE_RATE, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_MAX = + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_thermal_event - vendor subcmd attributes to + * report thermal events from driver to user space. + * enum values are used for NL attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT sub command. + */ +enum qca_wlan_vendor_attr_thermal_event { + QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_INVALID = 0, + /* Temperature value (degree Celsius) from driver. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_TEMPERATURE, + /* Indication of resume completion from power save mode. + * NLA_FLAG attribute. + */ + QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_RESUME_COMPLETE, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_MAX = + QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_AFTER_LAST - 1, +}; + +/** + * enum he_fragmentation_val - HE fragmentation support values + * Indicates level of dynamic fragmentation that is supported by + * a STA as a recipient. + * HE fragmentation values are defined in IEEE P802.11ax/D2.0, 9.4.2.237.2 + * (HE MAC Capabilities Information field) and are used in HE Capabilities + * element to advertise the support. These values are validated in the driver + * to check the device capability and advertised in the HE Capabilities + * element. These values are used to configure testbed device to allow the + * advertised hardware capabilities to be downgraded for testing purposes. + * + * @HE_FRAG_DISABLE: no support for dynamic fragmentation + * @HE_FRAG_LEVEL1: support for dynamic fragments that are + * contained within an MPDU or S-MPDU, no support for dynamic fragments + * within an A-MPDU that is not an S-MPDU. + * @HE_FRAG_LEVEL2: support for dynamic fragments that are + * contained within an MPDU or S-MPDU and support for up to one dynamic + * fragment for each MSDU, each A-MSDU if supported by the recipient, and + * each MMPDU within an A-MPDU or multi-TID A-MPDU that is not an + * MPDU or S-MPDU. + * @HE_FRAG_LEVEL3: support for dynamic fragments that are + * contained within an MPDU or S-MPDU and support for multiple dynamic + * fragments for each MSDU and for each A-MSDU if supported by the + * recipient within an A-MPDU or multi-TID AMPDU and up to one dynamic + * fragment for each MMPDU in a multi-TID A-MPDU that is not an S-MPDU. + */ +enum he_fragmentation_val { + HE_FRAG_DISABLE, + HE_FRAG_LEVEL1, + HE_FRAG_LEVEL2, + HE_FRAG_LEVEL3, +}; + +/** + * enum he_mcs_config - HE MCS support configuration + * + * Configures the HE Tx/Rx MCS map in HE capability IE for given bandwidth. + * These values are used in driver to configure the HE MCS map to advertise + * Tx/Rx MCS map in HE capability and these values are applied for all the + * streams supported by the device. To configure MCS for different bandwidths, + * vendor command needs to be sent using this attribute with appropriate value. + * For example, to configure HE_80_MCS_0_7, send vendor command using HE MCS + * attribute with HE_80_MCS0_7. And to configure HE MCS for HE_160_MCS0_11 + * send this command using HE MCS config attribute with value HE_160_MCS0_11. + * These values are used to configure testbed device to allow the advertised + * hardware capabilities to be downgraded for testing purposes. The enum values + * are defined such that BIT[1:0] indicates the MCS map value. Values 3,7 and + * 11 are not used as BIT[1:0] value is 3 which is used to disable MCS map. + * These values are validated in the driver before setting the MCS map and + * driver returns error if the input is other than these enum values. + * + * @HE_80_MCS0_7: support for HE 80/40/20 MHz MCS 0 to 7 + * @HE_80_MCS0_9: support for HE 80/40/20 MHz MCS 0 to 9 + * @HE_80_MCS0_11: support for HE 80/40/20 MHz MCS 0 to 11 + * @HE_160_MCS0_7: support for HE 160 MHz MCS 0 to 7 + * @HE_160_MCS0_9: support for HE 160 MHz MCS 0 to 9 + * @HE_160_MCS0_11: support for HE 160 MHz MCS 0 to 11 + * @HE_80P80_MCS0_7: support for HE 80p80 MHz MCS 0 to 7 + * @HE_80P80_MCS0_9: support for HE 80p80 MHz MCS 0 to 9 + * @HE_80P80_MCS0_11: support for HE 80p80 MHz MCS 0 to 11 + */ +enum he_mcs_config { + HE_80_MCS0_7 = 0, + HE_80_MCS0_9 = 1, + HE_80_MCS0_11 = 2, + HE_160_MCS0_7 = 4, + HE_160_MCS0_9 = 5, + HE_160_MCS0_11 = 6, + HE_80P80_MCS0_7 = 8, + HE_80P80_MCS0_9 = 9, + HE_80P80_MCS0_11 = 10, +}; + +/** + * enum qca_wlan_ba_session_config - BA session configuration + * + * Indicates the configuration values for BA session configuration attribute. + * + * @QCA_WLAN_ADD_BA: Establish a new BA session with given configuration. + * @QCA_WLAN_DELETE_BA: Delete the existing BA session for given TID. + */ +enum qca_wlan_ba_session_config { + QCA_WLAN_ADD_BA = 1, + QCA_WLAN_DELETE_BA = 2, +}; + +/** + * enum qca_wlan_ac_type - Access category type + * + * Indicates the access category type value. + * + * @QCA_WLAN_AC_BE: BE access category + * @QCA_WLAN_AC_BK: BK access category + * @QCA_WLAN_AC_VI: VI access category + * @QCA_WLAN_AC_VO: VO access category + * @QCA_WLAN_AC_ALL: All ACs + */ +enum qca_wlan_ac_type { + QCA_WLAN_AC_BE = 0, + QCA_WLAN_AC_BK = 1, + QCA_WLAN_AC_VI = 2, + QCA_WLAN_AC_VO = 3, + QCA_WLAN_AC_ALL = 4, +}; + +/** + * enum qca_wlan_he_ltf_cfg - HE LTF configuration + * + * Indicates the HE LTF configuration value. + * + * @QCA_WLAN_HE_LTF_AUTO: HE-LTF is automatically set to the mandatory HE-LTF, + * based on the GI setting + * @QCA_WLAN_HE_LTF_1X: 1X HE LTF is 3.2us LTF + * @QCA_WLAN_HE_LTF_2X: 2X HE LTF is 6.4us LTF + * @QCA_WLAN_HE_LTF_4X: 4X HE LTF is 12.8us LTF + */ +enum qca_wlan_he_ltf_cfg { + QCA_WLAN_HE_LTF_AUTO = 0, + QCA_WLAN_HE_LTF_1X = 1, + QCA_WLAN_HE_LTF_2X = 2, + QCA_WLAN_HE_LTF_4X = 3, +}; + +/** + * enum qca_wlan_he_mac_padding_dur - HE trigger frame MAC padding duration + * + * Indicates the HE trigger frame MAC padding duration value. + * + * @QCA_WLAN_HE_NO_ADDITIONAL_PROCESS_TIME: no additional time required to + * process the trigger frame. + * @QCA_WLAN_HE_8US_OF_PROCESS_TIME: indicates the 8us of processing time for + * trigger frame. + * @QCA_WLAN_HE_16US_OF_PROCESS_TIME: indicates the 16us of processing time for + * trigger frame. + */ +enum qca_wlan_he_mac_padding_dur { + QCA_WLAN_HE_NO_ADDITIONAL_PROCESS_TIME = 0, + QCA_WLAN_HE_8US_OF_PROCESS_TIME = 1, + QCA_WLAN_HE_16US_OF_PROCESS_TIME = 2, +}; + +/** + * enum qca_wlan_he_om_ctrl_ch_bw - HE OM control field BW configuration + * + * Indicates the HE Operating mode control channel width setting value. + * + * @QCA_WLAN_HE_OM_CTRL_BW_20M: Primary 20 MHz + * @QCA_WLAN_HE_OM_CTRL_BW_40M: Primary 40 MHz + * @QCA_WLAN_HE_OM_CTRL_BW_80M: Primary 80 MHz + * @QCA_WLAN_HE_OM_CTRL_BW_160M: 160 MHz and 80+80 MHz + */ +enum qca_wlan_he_om_ctrl_ch_bw { + QCA_WLAN_HE_OM_CTRL_BW_20M = 0, + QCA_WLAN_HE_OM_CTRL_BW_40M = 1, + QCA_WLAN_HE_OM_CTRL_BW_80M = 2, + QCA_WLAN_HE_OM_CTRL_BW_160M = 3, +}; + +/* Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION + */ +enum qca_wlan_vendor_attr_wifi_test_config { + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_INVALID = 0, + /* 8-bit unsigned value to configure the driver to enable/disable + * WMM feature. This attribute is used to configure testbed device. + * 1-enable, 0-disable + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WMM_ENABLE = 1, + + /* 8-bit unsigned value to configure the driver to accept/reject + * the addba request from peer. This attribute is used to configure + * the testbed device. + * 1-accept addba, 0-reject addba + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ACCEPT_ADDBA_REQ = 2, + + /* 8-bit unsigned value to configure the driver to send or not to + * send the addba request to peer. + * This attribute is used to configure the testbed device. + * 1-send addba, 0-do not send addba + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SEND_ADDBA_REQ = 3, + + /* 8-bit unsigned value to indicate the HE fragmentation support. + * Uses enum he_fragmentation_val values. + * This attribute is used to configure the testbed device to + * allow the advertised hardware capabilities to be downgraded + * for testing purposes. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_FRAGMENTATION = 4, + + /* 8-bit unsigned value to indicate the HE MCS support. + * Uses enum he_mcs_config values. + * This attribute is used to configure the testbed device to + * allow the advertised hardware capabilities to be downgraded + * for testing purposes. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MCS = 5, + + /* 8-bit unsigned value to configure the driver to allow or not to + * allow the connection with WEP/TKIP in HT/VHT/HE modes. + * This attribute is used to configure the testbed device. + * 1-allow WEP/TKIP in HT/VHT/HE, 0-do not allow WEP/TKIP. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WEP_TKIP_IN_HE = 6, + + /* 8-bit unsigned value to configure the driver to add a + * new BA session or delete the existing BA session for + * given TID. ADDBA command uses the buffer size and TID + * configuration if user specifies the values else default + * value for buffer size is used for all TIDs if the TID + * also not specified. For DEL_BA command TID value is + * required to process the command. + * Uses enum qca_wlan_ba_session_config values. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADD_DEL_BA_SESSION = 7, + + /* 16-bit unsigned value to configure the buffer size in addba + * request and response frames. + * This attribute is used to configure the testbed device. + * The range of the value is 0 to 256. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE = 8, + + /* 8-bit unsigned value to configure the buffer size in addba + * request and response frames. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BA_TID = 9, + + /* 8-bit unsigned value to configure the no ack policy. + * To configure no ack policy, access category value is + * required to process the command. + * This attribute is used to configure the testbed device. + * 1 - enable no ack, 0 - disable no ack. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_NO_ACK = 10, + + /* 8-bit unsigned value to configure the AC for no ack policy + * This attribute is used to configure the testbed device. + * Uses the enum qca_wlan_ac_type values. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_NO_ACK_AC = 11, + + /* 8-bit unsigned value to configure the HE LTF + * This attribute is used to configure the testbed device. + * Uses the enum qca_wlan_he_ltf_cfg values. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_LTF = 12, + + /* 8-bit unsigned value to configure the tx beamformee. + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_TX_BEAMFORMEE = 13, + + /* 8-bit unsigned value to configure the tx beamformee number + * of space-time streams. + * This attribute is used to configure the testbed device. + * The range of the value is 0 to 8. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_BEAMFORMEE_NSTS = 14, + + /* 8-bit unsigned value to configure the MU EDCA params for given AC + * This attribute is used to configure the testbed device. + * Uses the enum qca_wlan_ac_type values. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AC = 15, + + /* 8-bit unsigned value to configure the MU EDCA AIFSN for given AC + * To configure MU EDCA AIFSN value, MU EDCA access category value + * is required to process the command. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AIFSN = 16, + + /* 8-bit unsigned value to configure the MU EDCA ECW min value for + * given AC. + * To configure MU EDCA ECW min value, MU EDCA access category value + * is required to process the command. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_ECWMIN = 17, + + /* 8-bit unsigned value to configure the MU EDCA ECW max value for + * given AC. + * To configure MU EDCA ECW max value, MU EDCA access category value + * is required to process the command. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_ECWMAX = 18, + + /* 8-bit unsigned value to configure the MU EDCA timer for given AC + * To configure MU EDCA timer value, MU EDCA access category value + * is required to process the command. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_TIMER = 19, + + /* 8-bit unsigned value to configure the HE trigger frame MAC padding + * duration. + * This attribute is used to configure the testbed device. + * Uses the enum qca_wlan_he_mac_padding_dur values. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MAC_PADDING_DUR = 20, + + /* 8-bit unsigned value to override the MU EDCA params to defaults + * regardless of the AP beacon MU EDCA params. If it is enabled use + * the default values else use the MU EDCA params from AP beacon. + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OVERRIDE_MU_EDCA = 21, + + /* 8-bit unsigned value to configure the support for receiving + * an MPDU that contains an operating mode control subfield. + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_SUPP = 22, + + /* Nested attribute values required to setup the TWT session. + * enum qca_wlan_vendor_attr_twt_setup provides the necessary + * information to set up the session. It contains broadcast flags, + * set_up flags, trigger value, flow type, flow ID, wake interval + * exponent, protection, target wake time, wake duration, wake interval + * mantissa. These nested attributes are used to setup a host triggered + * TWT session. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP = 23, + + /* This nested attribute is used to terminate the current TWT session. + * It does not currently carry any attributes. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_TERMINATE = 24, + + /* This nested attribute is used to suspend the current TWT session. + * It does not currently carry any attributes. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SUSPEND = 25, + + /* Nested attribute values to indicate the request for resume. + * This attribute is used to resume the TWT session. + * enum qca_wlan_vendor_attr_twt_resume provides the necessary + * parameters required to resume the TWT session. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_RESUME = 26, + + /* 8-bit unsigned value to set the HE operating mode control + * (OM CTRL) Channel Width subfield. + * The Channel Width subfield indicates the operating channel width + * supported by the STA for both reception and transmission. + * Uses the enum qca_wlan_he_om_ctrl_ch_bw values. + * This setting is cleared with the + * QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG + * flag attribute to reset defaults. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_BW = 27, + + /* 8-bit unsigned value to configure the number of spatial + * streams in HE operating mode control field. + * This setting is cleared with the + * QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG + * flag attribute to reset defaults. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_NSS = 28, + + /* Flag attribute to configure the UL MU disable bit in + * HE operating mode control field. + * This setting is cleared with the + * QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG + * flag attribute to reset defaults. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_UL_MU_DISABLE = 29, + + /* Flag attribute to clear the previously set HE operating mode + * control field configuration. + * This attribute is used to configure the testbed device to reset + * defaults to clear any previously set HE operating mode control + * field configuration. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG = 30, + + /* 8-bit unsigned value to configure HE single user PPDU + * transmission. By default this setting is disabled and it + * is disabled in the reset defaults of the device configuration. + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_SUPPDU = 31, + + /* 8-bit unsigned value to configure action frame transmission + * in HE trigger based PPDU transmission. + * By default this setting is disabled and it is disabled in + * the reset defaults of the device configuration. + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_ACTION_TX_TB_PPDU = 32, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX = + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_bss_filter - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER. + * The user can add/delete the filter by specifying the BSSID/STA MAC address in + * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR, filter type in + * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE, add/delete action in + * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION in the request. The user can get the + * statistics of an unassociated station by specifying the MAC address in + * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR, station type in + * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE, GET action in + * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION in the request. The user also can get + * the statistics of all unassociated stations by specifying the Broadcast MAC + * address (ff:ff:ff:ff:ff:ff) in QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR with + * above procedure. In the response, driver shall specify statistics + * information nested in QCA_WLAN_VENDOR_ATTR_BSS_FILTER_STA_STATS. + */ +enum qca_wlan_vendor_attr_bss_filter { + QCA_WLAN_VENDOR_ATTR_BSS_FILTER_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR = 1, + /* Other BSS filter type, unsigned 8 bit value. One of the values + * in enum qca_wlan_vendor_bss_filter_type. + */ + QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE = 2, + /* Other BSS filter action, unsigned 8 bit value. One of the values + * in enum qca_wlan_vendor_bss_filter_action. + */ + QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION = 3, + /* Array of nested attributes where each entry is the statistics + * information of the specified station that belong to another BSS. + * Attributes for each entry are taken from enum + * qca_wlan_vendor_bss_filter_sta_stats. + * Other BSS station configured in + * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER with filter type + * QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA. + * Statistics returned by QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER + * with filter action QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET. + */ + QCA_WLAN_VENDOR_ATTR_BSS_FILTER_STA_STATS = 4, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_BSS_FILTER_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAX = + QCA_WLAN_VENDOR_ATTR_BSS_FILTER_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_bss_filter_type - Type of + * filter used in other BSS filter operations. Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER. + * + * @QCA_WLAN_VENDOR_BSS_FILTER_TYPE_BSSID: BSSID filter + * @QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA: Station MAC address filter + */ +enum qca_wlan_vendor_bss_filter_type { + QCA_WLAN_VENDOR_BSS_FILTER_TYPE_BSSID, + QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA, +}; + +/** + * enum qca_wlan_vendor_bss_filter_action - Type of + * action in other BSS filter operations. Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER. + * + * @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_ADD: Add filter + * @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_DEL: Delete filter + * @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET: Get the statistics + */ +enum qca_wlan_vendor_bss_filter_action { + QCA_WLAN_VENDOR_BSS_FILTER_ACTION_ADD, + QCA_WLAN_VENDOR_BSS_FILTER_ACTION_DEL, + QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET, +}; + +/** + * enum qca_wlan_vendor_bss_filter_sta_stats - Attributes for + * the statistics of a specific unassociated station belonging to another BSS. + * The statistics provides information of the unassociated station + * filtered by other BSS operation - such as MAC, signal value. + * Used by the vendor command QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER. + * + * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAC: MAC address of the station. + * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI: Last received signal strength + * of the station. Unsigned 8 bit number containing RSSI. + * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI_TS: Time stamp of the host + * driver for the last received RSSI. Unsigned 64 bit number containing + * nanoseconds from the boottime. + */ +enum qca_wlan_vendor_bss_filter_sta_stats { + QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_INVALID = 0, + QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAC = 1, + QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI = 2, + QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI_TS = 3, + + /* keep last */ + QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_AFTER_LAST, + QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAX = + QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_AFTER_LAST - 1 +}; + +/* enum qca_wlan_nan_subcmd_type - Type of NAN command used by attribute + * QCA_WLAN_VENDOR_ATTR_NAN_SUBCMD_TYPE as a part of vendor command + * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT. + */ +enum qca_wlan_nan_ext_subcmd_type { + /* Subcmd of type NAN Enable Request */ + QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ = 1, + /* Subcmd of type NAN Disable Request */ + QCA_WLAN_NAN_EXT_SUBCMD_TYPE_DISABLE_REQ = 2, +}; + +/** + * enum qca_wlan_vendor_attr_nan_params - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT. + */ +enum qca_wlan_vendor_attr_nan_params { + QCA_WLAN_VENDOR_ATTR_NAN_INVALID = 0, + /* Carries NAN command for firmware component. Every vendor command + * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT must contain this attribute with a + * payload containing the NAN command. NLA_BINARY attribute. + */ + QCA_WLAN_VENDOR_ATTR_NAN_CMD_DATA = 1, + /* Indicates the type of NAN command sent with + * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT. enum qca_wlan_nan_ext_subcmd_type + * describes the possible range of values. This attribute is mandatory + * if the command being issued is either + * QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ or + * QCA_WLAN_NAN_EXT_SUBCMD_TYPE_DISABLE_REQ. NLA_U32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_NAN_SUBCMD_TYPE = 2, + /* Frequency (in MHz) of primary NAN discovery social channel in 2.4 GHz + * band. This attribute is mandatory when command type is + * QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ. NLA_U32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_NAN_DISC_24GHZ_BAND_FREQ = 3, + /* Frequency (in MHz) of secondary NAN discovery social channel in 5 GHz + * band. This attribute is optional and should be included when command + * type is QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ and NAN discovery + * has to be started on 5GHz along with 2.4GHz. NLA_U32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_NAN_DISC_5GHZ_BAND_FREQ = 4, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_MAX = + QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_twt_setup: Represents attributes for + * TWT (Target Wake Time) setup request. These attributes are sent as part of + * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP and + * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST: Flag attribute. + * Disable (flag attribute not present) - Individual TWT + * Enable (flag attribute present) - Broadcast TWT. + * Individual means the session is between the STA and the AP. + * This session is established using a separate negotiation between + * STA and AP. + * Broadcast means the session is across multiple STAs and an AP. The + * configuration parameters are announced in Beacon frames by the AP. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE: Required (u8). + * Unsigned 8-bit qca_wlan_vendor_twt_setup_req_type to + * specify the TWT request type + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER: Flag attribute + * Enable (flag attribute present) - TWT with trigger support. + * Disable (flag attribute not present) - TWT without trigger support. + * Trigger means the AP will send the trigger frame to allow STA to send data. + * Without trigger, the STA will wait for the MU EDCA timer before + * transmitting the data. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE: Required (u8) + * 0 - Announced TWT - In this mode, STA may skip few service periods to + * save more power. If STA wants to wake up, it will send a PS-POLL/QoS + * NULL frame to AP. + * 1 - Unannounced TWT - The STA will wakeup during every SP. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID: Optional (u8) + * Flow ID is the unique identifier for each TWT session. + * Currently this is not required and dialog ID will be set to zero. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP: Required (u8) + * This attribute (exp) is used along with the mantissa to derive the + * wake interval using the following formula: + * pow(2,exp) = wake_intvl_us/wake_intvl_mantis + * Wake interval is the interval between 2 successive SP. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION: Flag attribute + * Enable (flag attribute present) - Protection required. + * Disable (flag attribute not present) - Protection not required. + * If protection is enabled, then the AP will use protection + * mechanism using RTS/CTS to self to reserve the airtime. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME: Optional (u32) + * This attribute is used as the SP offset which is the offset from + * TSF after which the wake happens. The units are in microseconds. If + * this attribute is not provided, then the value will be set to zero. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION: Required (u32) + * This is the duration of the service period. The units are in TU. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA: Required (u32) + * This attribute is used to configure wake interval mantissa. + * The units are in TU. + */ +enum qca_wlan_vendor_attr_twt_setup { + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST = 1, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE = 2, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER = 3, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE = 4, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID = 5, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP = 6, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION = 7, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME = 8, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION = 9, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA = 10, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX = + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_twt_resume: Represents attributes for + * TWT (Target Wake Time) resume request. These attributes are sent as part of + * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_RESUME and + * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT: Optional (u8) + * This attribute is used as the SP offset which is the offset from + * TSF after which the wake happens. The units are in microseconds. + * If this attribute is not provided, then the value will be set to + * zero. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE: Required (u32) + * This attribute represents the next TWT subfield size. + */ +enum qca_wlan_vendor_attr_twt_resume { + QCA_WLAN_VENDOR_ATTR_TWT_RESUME_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT = 1, + QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE = 2, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TWT_RESUME_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TWT_RESUME_MAX = + QCA_WLAN_VENDOR_ATTR_TWT_RESUME_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_twt_setup_req_type - Required (u8) + * Represents the setup type being requested for TWT. + * @QCA_WLAN_VENDOR_TWT_SETUP_REQUEST: STA is not specifying all the TWT + * parameters but relying on AP to fill the parameters during the negotiation. + * @QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST: STA will provide all the suggested + * values which the AP may accept or AP may provide alternative parameters + * which the STA may accept. + * @QCA_WLAN_VENDOR_TWT_SETUP_DEMAND: STA is not willing to accept any + * alternate parameters than the requested ones. + */ +enum qca_wlan_vendor_twt_setup_req_type { + QCA_WLAN_VENDOR_TWT_SETUP_REQUEST = 1, + QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST = 2, + QCA_WLAN_VENDOR_TWT_SETUP_DEMAND = 3, +}; + +/** + * enum qca_wlan_roam_scan_event_type - Type of roam scan event + * + * Indicates the type of roam scan event sent by firmware/driver. + * + * @QCA_WLAN_ROAM_SCAN_TRIGGER_EVENT: Roam scan trigger event type. + * @QCA_WLAN_ROAM_SCAN_STOP_EVENT: Roam scan stopped event type. + */ +enum qca_wlan_roam_scan_event_type { + QCA_WLAN_ROAM_SCAN_TRIGGER_EVENT = 0, + QCA_WLAN_ROAM_SCAN_STOP_EVENT = 1, +}; + +/** + * enum qca_wlan_roam_scan_trigger_reason - Roam scan trigger reason + * + * Indicates the reason for triggering roam scan by firmware/driver. + * + * @QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_LOW_RSSI: Due to low RSSI of current AP. + * @QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_HIGH_PER: Due to high packet error rate. + */ +enum qca_wlan_roam_scan_trigger_reason { + QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_LOW_RSSI = 0, + QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_HIGH_PER = 1, +}; + +/** + * enum qca_wlan_vendor_attr_roam_scan - Vendor subcmd attributes to report + * roam scan related details from driver/firmware to user space. enum values + * are used for NL attributes sent with + * %QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT sub command. + */ +enum qca_wlan_vendor_attr_roam_scan { + QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_INVALID = 0, + /* Encapsulates type of roam scan event being reported. enum + * qca_wlan_roam_scan_event_type describes the possible range of + * values. u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_EVENT_TYPE = 1, + /* Encapsulates reason for triggering roam scan. enum + * qca_wlan_roam_scan_trigger_reason describes the possible range of + * values. u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_TRIGGER_REASON = 2, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_MAX = + QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_cfr_method - QCA vendor CFR methods used by + * attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD as part of vendor + * command QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG. + */ +enum qca_wlan_vendor_cfr_method { + /* CFR method using QOS Null frame */ + QCA_WLAN_VENDOR_CFR_METHOD_QOS_NULL = 0, +}; + +/** + * enum qca_wlan_vendor_peer_cfr_capture_attr - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG to configure peer + * Channel Frequency Response capture parameters and enable periodic CFR + * capture. + */ +enum qca_wlan_vendor_peer_cfr_capture_attr { + QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_INVALID = 0, + /* 6-byte MAC address of the peer. + * This attribute is mandatory. + */ + QCA_WLAN_VENDOR_ATTR_CFR_PEER_MAC_ADDR = 1, + /* Enable peer CFR Capture, flag attribute. + * This attribute is mandatory to enable peer CFR capture. + * If this attribute is not present, peer CFR capture is disabled. + */ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE = 2, + /* BW of measurement, attribute uses the values in enum nl80211_chan_width + * Supported values: 20, 40, 80, 80+80, 160. + * Note that all targets may not support all bandwidths. + * u8 attribute. This attribute is mandatory if attribute + * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used. + */ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_BANDWIDTH = 3, + /* Periodicity of CFR measurement in msec. + * Periodicity should be a multiple of Base timer. + * Current Base timer value supported is 10 msecs (default). + * 0 for one shot capture. u32 attribute. + * This attribute is mandatory if attribute + * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used. + */ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_PERIODICITY = 4, + /* Method used to capture Channel Frequency Response. + * Attribute uses the values defined in enum qca_wlan_vendor_cfr_method. + * u8 attribute. This attribute is mandatory if attribute + * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used. + */ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD = 5, + /* Enable periodic CFR capture, flag attribute. + * This attribute is mandatory to enable Periodic CFR capture. + * If this attribute is not present, periodic CFR capture is disabled. + */ + QCA_WLAN_VENDOR_ATTR_PERIODIC_CFR_CAPTURE_ENABLE = 6, + + /* Keep last */ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX = + QCA_WLAN_VENDOR_ATTR_PEER_CFR_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_throughput_level - Current throughput level + * + * Indicates the current level of throughput calculated by the driver. The + * driver may choose different thresholds to decide whether the throughput level + * is low or medium or high based on variety of parameters like physical link + * capacity of the current connection, the number of packets being dispatched + * per second, etc. The throughput level events might not be consistent with the + * actual current throughput value being observed. + * + * @QCA_WLAN_THROUGHPUT_LEVEL_LOW: Low level of throughput + * @QCA_WLAN_THROUGHPUT_LEVEL_MEDIUM: Medium level of throughput + * @QCA_WLAN_THROUGHPUT_LEVEL_HIGH: High level of throughput + */ +enum qca_wlan_throughput_level { + QCA_WLAN_THROUGHPUT_LEVEL_LOW = 0, + QCA_WLAN_THROUGHPUT_LEVEL_MEDIUM = 1, + QCA_WLAN_THROUGHPUT_LEVEL_HIGH = 2, +}; + +/** + * enum qca_wlan_vendor_attr_throughput_change - Vendor subcmd attributes to + * report throughput changes from the driver to user space. enum values are used + * for netlink attributes sent with + * %QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT sub command. + */ +enum qca_wlan_vendor_attr_throughput_change { + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_INVALID = 0, + /* Indicates the direction of throughput in which the change is being + * reported. u8 attribute. Value is 0 for TX and 1 for RX. + */ + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_DIRECTION = 1, + /* Indicates the newly observed throughput level. enum + * qca_wlan_throughput_level describes the possible range of values. + * u8 attribute. + */ + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_THROUGHPUT_LEVEL = 2, + /* Indicates the driver's guidance on the new value to be set to + * kernel's TCP parameter tcp_limit_output_bytes. u32 attribute. The + * driver may optionally include this attribute. + */ + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_LIMIT_OUTPUT_BYTES = 3, + /* Indicates the driver's guidance on the new value to be set to + * kernel's TCP parameter tcp_adv_win_scale. s8 attribute. Possible + * values are from -31 to 31. The driver may optionally include this + * attribute. + */ + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_ADV_WIN_SCALE = 4, + /* Indicates the driver's guidance on the new value to be set to + * kernel's TCP parameter tcp_delack_seg. u32 attribute. The driver may + * optionally include this attribute. + */ + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_DELACK_SEG = 5, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_MAX = + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_AFTER_LAST - 1, +}; + +/** + * enum qca_coex_config_profiles - This enum defines different types of + * traffic streams that can be prioritized one over the other during coex + * scenarios. + * The types defined in this enum are categorized in the below manner. + * 0 - 31 values corresponds to WLAN + * 32 - 63 values corresponds to BT + * 64 - 95 values corresponds to Zigbee + * @QCA_WIFI_STA_DISCOVERY: Prioritize discovery frames for WLAN STA + * @QCA_WIFI_STA_CONNECTION: Prioritize connection frames for WLAN STA + * @QCA_WIFI_STA_CLASS_3_MGMT: Prioritize class 3 mgmt frames for WLAN STA + * @QCA_WIFI_STA_DATA : Prioritize data frames for WLAN STA + * @QCA_WIFI_STA_ALL: Priritize all frames for WLAN STA + * @QCA_WIFI_SAP_DISCOVERY: Prioritize discovery frames for WLAN SAP + * @QCA_WIFI_SAP_CONNECTION: Prioritize connection frames for WLAN SAP + * @QCA_WIFI_SAP_CLASS_3_MGMT: Prioritize class 3 mgmt frames for WLAN SAP + * @QCA_WIFI_SAP_DATA: Prioritize data frames for WLAN SAP + * @QCA_WIFI_SAP_ALL: Prioritize all frames for WLAN SAP + * @QCA_BT_A2DP: Prioritize BT A2DP + * @QCA_BT_BLE: Prioritize BT BLE + * @QCA_BT_SCO: Prioritize BT SCO + * @QCA_ZB_LOW: Prioritize Zigbee Low + * @QCA_ZB_HIGH: Prioritize Zigbee High + */ +enum qca_coex_config_profiles { + /* 0 - 31 corresponds to WLAN */ + QCA_WIFI_STA_DISCOVERY = 0, + QCA_WIFI_STA_CONNECTION = 1, + QCA_WIFI_STA_CLASS_3_MGMT = 2, + QCA_WIFI_STA_DATA = 3, + QCA_WIFI_STA_ALL = 4, + QCA_WIFI_SAP_DISCOVERY = 5, + QCA_WIFI_SAP_CONNECTION = 6, + QCA_WIFI_SAP_CLASS_3_MGMT = 7, + QCA_WIFI_SAP_DATA = 8, + QCA_WIFI_SAP_ALL = 9, + /* 32 - 63 corresponds to BT */ + QCA_BT_A2DP = 32, + QCA_BT_BLE = 33, + QCA_BT_SCO = 34, + /* 64 - 95 corresponds to Zigbee */ + QCA_ZB_LOW = 64, + QCA_ZB_HIGH = 65 +}; + +/** + * enum qca_vendor_attr_coex_config - Specifies vendor coex config attributes + * + * @QCA_VENDOR_ATTR_COEX_CONFIG_PROFILES: This attribute contains variable + * length array of 8-bit values from enum qca_coex_config_profiles. + * FW will prioritize the profiles in the order given in the array encapsulated + * in this attribute. + * For example: + * ----------------------------------------------------------------------- + * | 1 | 34 | 32 | 65 | + * ----------------------------------------------------------------------- + * If the attribute contains the values defined in above array then it means + * 1) Wifi STA connection has priority over BT_SCO, BT_A2DP and ZIGBEE HIGH. + * 2) BT_SCO has priority over BT_A2DP. + * 3) BT_A2DP has priority over ZIGBEE HIGH. + * Profiles which are not listed in this array shall not be preferred over the + * profiles which are listed in the array as a part of this attribute. + */ +enum qca_vendor_attr_coex_config { + QCA_VENDOR_ATTR_COEX_CONFIG_INVALID = 0, + QCA_VENDOR_ATTR_COEX_CONFIG_PROFILES = 1, + + /* Keep last */ + QCA_VENDOR_ATTR_COEX_CONFIG_AFTER_LAST, + QCA_VENDOR_ATTR_COEX_CONFIG_MAX = + QCA_VENDOR_ATTR_COEX_CONFIG_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_link_properties - Represent the link properties. + * + * @QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR: MAC address of the peer + * (STA/AP) for the connected link. + * @QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_STA_FLAGS: Attribute containing a + * &struct nl80211_sta_flag_update for the respective connected link. MAC + * address of the peer represented by + * QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR. + */ +enum qca_wlan_vendor_attr_link_properties { + QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_INVALID = 0, + /* 1 - 3 are reserved */ + QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR = 4, + QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_STA_FLAGS = 5, + + /* Keep last */ + QCA_VENDOR_ATTR_LINK_PROPERTIES_AFTER_LAST, + QCA_VENDOR_ATTR_LINK_PROPERTIES_MAX = + QCA_VENDOR_ATTR_LINK_PROPERTIES_AFTER_LAST - 1, +}; + #endif /* QCA_VENDOR_H */ diff --git a/src/common/sae.c b/src/common/sae.c index 9f70f036ba76..981e788dc751 100644 --- a/src/common/sae.c +++ b/src/common/sae.c @@ -29,6 +29,8 @@ int sae_set_group(struct sae_data *sae, int group) /* First, check if this is an ECC group */ tmp->ec = crypto_ec_init(group); if (tmp->ec) { + wpa_printf(MSG_DEBUG, "SAE: Selecting supported ECC group %d", + group); sae->group = group; tmp->prime_len = crypto_ec_prime_len(tmp->ec); tmp->prime = crypto_ec_get_prime(tmp->ec); @@ -39,6 +41,8 @@ int sae_set_group(struct sae_data *sae, int group) /* Not an ECC group, check FFC */ tmp->dh = dh_groups_get(group); if (tmp->dh) { + wpa_printf(MSG_DEBUG, "SAE: Selecting supported FFC group %d", + group); sae->group = group; tmp->prime_len = tmp->dh->prime_len; if (tmp->prime_len > SAE_MAX_PRIME_LEN) { @@ -66,6 +70,8 @@ int sae_set_group(struct sae_data *sae, int group) } /* Unsupported group */ + wpa_printf(MSG_DEBUG, + "SAE: Group %d not supported by the crypto library", group); return -1; } @@ -88,6 +94,7 @@ void sae_clear_temp_data(struct sae_data *sae) crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0); crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0); wpabuf_free(tmp->anti_clogging_token); + os_free(tmp->pw_id); bin_clear_free(tmp, sizeof(*tmp)); sae->tmp = NULL; } @@ -417,12 +424,13 @@ static int get_random_qr_qnr(const u8 *prime, size_t prime_len, static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, const u8 *addr2, const u8 *password, - size_t password_len) + size_t password_len, const char *identifier) { u8 counter, k = 40; u8 addrs[2 * ETH_ALEN]; - const u8 *addr[2]; - size_t len[2]; + const u8 *addr[3]; + size_t len[3]; + size_t num_elem; u8 dummy_password[32]; size_t dummy_password_len; int pwd_seed_odd = 0; @@ -454,10 +462,13 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", password, password_len); + if (identifier) + wpa_printf(MSG_DEBUG, "SAE: password identifier: %s", + identifier); /* * H(salt, ikm) = HMAC-SHA256(salt, ikm) - * base = password + * base = password [|| identifier] * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), * base || counter) */ @@ -465,8 +476,15 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, addr[0] = password; len[0] = password_len; - addr[1] = &counter; - len[1] = sizeof(counter); + num_elem = 1; + if (identifier) { + addr[num_elem] = (const u8 *) identifier; + len[num_elem] = os_strlen(identifier); + num_elem++; + } + addr[num_elem] = &counter; + len[num_elem] = sizeof(counter); + num_elem++; /* * Continue for at least k iterations to protect against side-channel @@ -484,8 +502,8 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, } wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); - if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, - pwd_seed) < 0) + if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, + addr, len, pwd_seed) < 0) break; res = sae_test_pwd_seed_ecc(sae, pwd_seed, @@ -544,12 +562,13 @@ fail: static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, const u8 *addr2, const u8 *password, - size_t password_len) + size_t password_len, const char *identifier) { u8 counter; u8 addrs[2 * ETH_ALEN]; - const u8 *addr[2]; - size_t len[2]; + const u8 *addr[3]; + size_t len[3]; + size_t num_elem; int found = 0; if (sae->tmp->pwe_ffc == NULL) { @@ -564,14 +583,21 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, /* * H(salt, ikm) = HMAC-SHA256(salt, ikm) * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), - * password || counter) + * password [|| identifier] || counter) */ sae_pwd_seed_key(addr1, addr2, addrs); addr[0] = password; len[0] = password_len; - addr[1] = &counter; - len[1] = sizeof(counter); + num_elem = 1; + if (identifier) { + addr[num_elem] = (const u8 *) identifier; + len[num_elem] = os_strlen(identifier); + num_elem++; + } + addr[num_elem] = &counter; + len[num_elem] = sizeof(counter); + num_elem++; for (counter = 1; !found; counter++) { u8 pwd_seed[SHA256_MAC_LEN]; @@ -584,8 +610,8 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, } wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); - if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, - pwd_seed) < 0) + if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, + addr, len, pwd_seed) < 0) break; res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc); if (res < 0) @@ -696,13 +722,15 @@ fail: int sae_prepare_commit(const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len, - struct sae_data *sae) + const char *identifier, struct sae_data *sae) { if (sae->tmp == NULL || (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, - password_len) < 0) || + password_len, + identifier) < 0) || (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, - password_len) < 0) || + password_len, + identifier) < 0) || sae_derive_commit(sae) < 0) return -1; return 0; @@ -842,7 +870,7 @@ int sae_process_commit(struct sae_data *sae) void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, - const struct wpabuf *token) + const struct wpabuf *token, const char *identifier) { u8 *pos; @@ -876,6 +904,16 @@ void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, wpa_hexdump(MSG_DEBUG, "SAE: own commit-element", pos, sae->tmp->prime_len); } + + if (identifier) { + /* Password Identifier element */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + os_strlen(identifier)); + wpabuf_put_u8(buf, WLAN_EID_EXT_PASSWORD_IDENTIFIER); + wpabuf_put_str(buf, identifier); + wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s", + identifier); + } } @@ -921,25 +959,70 @@ u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group) } +static int sae_is_password_id_elem(const u8 *pos, const u8 *end) +{ + return end - pos >= 3 && + pos[0] == WLAN_EID_EXTENSION && + pos[1] >= 1 && + end - pos - 2 >= pos[1] && + pos[2] == WLAN_EID_EXT_PASSWORD_IDENTIFIER; +} + + static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, const u8 *end, const u8 **token, size_t *token_len) { - if ((sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end - *pos) { - size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) * - sae->tmp->prime_len); - wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); - if (token) - *token = *pos; - if (token_len) - *token_len = tlen; - *pos += tlen; - } else { - if (token) - *token = NULL; - if (token_len) - *token_len = 0; + size_t scalar_elem_len, tlen; + const u8 *elem; + + if (token) + *token = NULL; + if (token_len) + *token_len = 0; + + scalar_elem_len = (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len; + if (scalar_elem_len >= (size_t) (end - *pos)) + return; /* No extra data beyond peer scalar and element */ + + /* It is a bit difficult to parse this now that there is an + * optional variable length Anti-Clogging Token field and + * optional variable length Password Identifier element in the + * frame. We are sending out fixed length Anti-Clogging Token + * fields, so use that length as a requirement for the received + * token and check for the presence of possible Password + * Identifier element based on the element header information. + */ + tlen = end - (*pos + scalar_elem_len); + + if (tlen < SHA256_MAC_LEN) { + wpa_printf(MSG_DEBUG, + "SAE: Too short optional data (%u octets) to include our Anti-Clogging Token", + (unsigned int) tlen); + return; + } + + elem = *pos + scalar_elem_len; + if (sae_is_password_id_elem(elem, end)) { + /* Password Identifier element takes out all available + * extra octets, so there can be no Anti-Clogging token in + * this frame. */ + return; + } + + elem += SHA256_MAC_LEN; + if (sae_is_password_id_elem(elem, end)) { + /* Password Identifier element is included in the end, so + * remove its length from the Anti-Clogging token field. */ + tlen -= 2 + elem[1]; } + + wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); + if (token) + *token = *pos; + if (token_len) + *token_len = tlen; + *pos += tlen; } @@ -991,12 +1074,12 @@ static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, } -static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, +static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos, const u8 *end) { u8 prime[SAE_MAX_ECC_PRIME_LEN]; - if (2 * sae->tmp->prime_len > end - pos) { + if (2 * sae->tmp->prime_len > end - *pos) { wpa_printf(MSG_DEBUG, "SAE: Not enough data for " "commit-element"); return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -1007,8 +1090,8 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, return WLAN_STATUS_UNSPECIFIED_FAILURE; /* element x and y coordinates < p */ - if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 || - os_memcmp(pos + sae->tmp->prime_len, prime, + if (os_memcmp(*pos, prime, sae->tmp->prime_len) >= 0 || + os_memcmp(*pos + sae->tmp->prime_len, prime, sae->tmp->prime_len) >= 0) { wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer " "element"); @@ -1016,13 +1099,13 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, } wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)", - pos, sae->tmp->prime_len); + *pos, sae->tmp->prime_len); wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)", - pos + sae->tmp->prime_len, sae->tmp->prime_len); + *pos + sae->tmp->prime_len, sae->tmp->prime_len); crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0); sae->tmp->peer_commit_element_ecc = - crypto_ec_point_from_bin(sae->tmp->ec, pos); + crypto_ec_point_from_bin(sae->tmp->ec, *pos); if (sae->tmp->peer_commit_element_ecc == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -1032,27 +1115,29 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, return WLAN_STATUS_UNSPECIFIED_FAILURE; } + *pos += 2 * sae->tmp->prime_len; + return WLAN_STATUS_SUCCESS; } -static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, +static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 **pos, const u8 *end) { struct crypto_bignum *res, *one; const u8 one_bin[1] = { 0x01 }; - if (sae->tmp->prime_len > end - pos) { + if (sae->tmp->prime_len > end - *pos) { wpa_printf(MSG_DEBUG, "SAE: Not enough data for " "commit-element"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } - wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", pos, + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", *pos, sae->tmp->prime_len); crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0); sae->tmp->peer_commit_element_ffc = - crypto_bignum_init_set(pos, sae->tmp->prime_len); + crypto_bignum_init_set(*pos, sae->tmp->prime_len); if (sae->tmp->peer_commit_element_ffc == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; /* 1 < element < p - 1 */ @@ -1080,11 +1165,13 @@ static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, } crypto_bignum_deinit(res, 0); + *pos += sae->tmp->prime_len; + return WLAN_STATUS_SUCCESS; } -static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos, +static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos, const u8 *end) { if (sae->tmp->dh) @@ -1093,6 +1180,44 @@ static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos, } +static int sae_parse_password_identifier(struct sae_data *sae, + const u8 *pos, const u8 *end) +{ + wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", + pos, end - pos); + if (!sae_is_password_id_elem(pos, end)) { + if (sae->tmp->pw_id) { + wpa_printf(MSG_DEBUG, + "SAE: No Password Identifier included, but expected one (%s)", + sae->tmp->pw_id); + return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; + } + os_free(sae->tmp->pw_id); + sae->tmp->pw_id = NULL; + return WLAN_STATUS_SUCCESS; /* No Password Identifier */ + } + + if (sae->tmp->pw_id && + (pos[1] - 1 != (int) os_strlen(sae->tmp->pw_id) || + os_memcmp(sae->tmp->pw_id, pos + 3, pos[1] - 1) != 0)) { + wpa_printf(MSG_DEBUG, + "SAE: The included Password Identifier does not match the expected one (%s)", + sae->tmp->pw_id); + return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; + } + + os_free(sae->tmp->pw_id); + sae->tmp->pw_id = os_malloc(pos[1]); + if (!sae->tmp->pw_id) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + os_memcpy(sae->tmp->pw_id, pos + 3, pos[1] - 1); + sae->tmp->pw_id[pos[1] - 1] = '\0'; + wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier", + sae->tmp->pw_id, pos[1] - 1); + return WLAN_STATUS_SUCCESS; +} + + u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, const u8 **token, size_t *token_len, int *allowed_groups) { @@ -1116,7 +1241,12 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, return res; /* commit-element */ - res = sae_parse_commit_element(sae, pos, end); + res = sae_parse_commit_element(sae, &pos, end); + if (res != WLAN_STATUS_SUCCESS) + return res; + + /* Optional Password Identifier element */ + res = sae_parse_password_identifier(sae, pos, end); if (res != WLAN_STATUS_SUCCESS) return res; @@ -1235,7 +1365,8 @@ void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) /* Send-Confirm */ sc = wpabuf_put(buf, 0); wpabuf_put_le16(buf, sae->send_confirm); - sae->send_confirm++; + if (sae->send_confirm < 0xffff) + sae->send_confirm++; if (sae->tmp->ec) sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar, @@ -1292,3 +1423,19 @@ int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) return 0; } + + +const char * sae_state_txt(enum sae_state state) +{ + switch (state) { + case SAE_NOTHING: + return "Nothing"; + case SAE_COMMITTED: + return "Committed"; + case SAE_CONFIRMED: + return "Confirmed"; + case SAE_ACCEPTED: + return "Accepted"; + } + return "?"; +} diff --git a/src/common/sae.h b/src/common/sae.h index a4270bc22d14..3fbcb58d155a 100644 --- a/src/common/sae.h +++ b/src/common/sae.h @@ -39,16 +39,22 @@ struct sae_temporary_data { struct crypto_bignum *prime_buf; struct crypto_bignum *order_buf; struct wpabuf *anti_clogging_token; + char *pw_id; +}; + +enum sae_state { + SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED }; struct sae_data { - enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state; + enum sae_state state; u16 send_confirm; u8 pmk[SAE_PMK_LEN]; u8 pmkid[SAE_PMKID_LEN]; struct crypto_bignum *peer_commit_scalar; int group; - int sync; + unsigned int sync; /* protocol instance variable: Sync */ + u16 rc; /* protocol instance variable: Rc (received send-confirm) */ struct sae_temporary_data *tmp; }; @@ -58,14 +64,15 @@ void sae_clear_data(struct sae_data *sae); int sae_prepare_commit(const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len, - struct sae_data *sae); + const char *identifier, struct sae_data *sae); int sae_process_commit(struct sae_data *sae); void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, - const struct wpabuf *token); + const struct wpabuf *token, const char *identifier); u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, const u8 **token, size_t *token_len, int *allowed_groups); void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf); int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len); u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group); +const char * sae_state_txt(enum sae_state state); #endif /* SAE_H */ diff --git a/src/common/version.h b/src/common/version.h index 75e5c6e006cc..2f47903d407c 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -9,6 +9,6 @@ #define GIT_VERSION_STR_POSTFIX "" #endif /* GIT_VERSION_STR_POSTFIX */ -#define VERSION_STR "2.6" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX +#define VERSION_STR "2.7" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX #endif /* VERSION_H */ diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 299b8bbee031..2d989048ac94 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -1,6 +1,6 @@ /* * WPA/RSN - Shared functions for supplicant and authenticator - * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -13,6 +13,7 @@ #include "crypto/sha1.h" #include "crypto/sha256.h" #include "crypto/sha384.h" +#include "crypto/sha512.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" #include "ieee802_11_defs.h" @@ -20,27 +21,151 @@ #include "wpa_common.h" -static unsigned int wpa_kck_len(int akmp) +static unsigned int wpa_kck_len(int akmp, size_t pmk_len) { - if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + switch (akmp) { + case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + return 24; + case WPA_KEY_MGMT_FILS_SHA256: + case WPA_KEY_MGMT_FT_FILS_SHA256: + case WPA_KEY_MGMT_FILS_SHA384: + case WPA_KEY_MGMT_FT_FILS_SHA384: + return 0; + case WPA_KEY_MGMT_DPP: + return pmk_len / 2; + case WPA_KEY_MGMT_OWE: + return pmk_len / 2; + default: + return 16; + } +} + + +#ifdef CONFIG_IEEE80211R +static unsigned int wpa_kck2_len(int akmp) +{ + switch (akmp) { + case WPA_KEY_MGMT_FT_FILS_SHA256: + return 16; + case WPA_KEY_MGMT_FT_FILS_SHA384: return 24; - return 16; + default: + return 0; + } +} +#endif /* CONFIG_IEEE80211R */ + + +static unsigned int wpa_kek_len(int akmp, size_t pmk_len) +{ + switch (akmp) { + case WPA_KEY_MGMT_FILS_SHA384: + case WPA_KEY_MGMT_FT_FILS_SHA384: + return 64; + case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: + case WPA_KEY_MGMT_FILS_SHA256: + case WPA_KEY_MGMT_FT_FILS_SHA256: + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + return 32; + case WPA_KEY_MGMT_DPP: + return pmk_len <= 32 ? 16 : 32; + case WPA_KEY_MGMT_OWE: + return pmk_len <= 32 ? 16 : 32; + default: + return 16; + } } -static unsigned int wpa_kek_len(int akmp) +#ifdef CONFIG_IEEE80211R +static unsigned int wpa_kek2_len(int akmp) { - if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + switch (akmp) { + case WPA_KEY_MGMT_FT_FILS_SHA256: + return 16; + case WPA_KEY_MGMT_FT_FILS_SHA384: return 32; - return 16; + default: + return 0; + } } +#endif /* CONFIG_IEEE80211R */ -unsigned int wpa_mic_len(int akmp) +unsigned int wpa_mic_len(int akmp, size_t pmk_len) { - if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + switch (akmp) { + case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: return 24; - return 16; + case WPA_KEY_MGMT_FILS_SHA256: + case WPA_KEY_MGMT_FILS_SHA384: + case WPA_KEY_MGMT_FT_FILS_SHA256: + case WPA_KEY_MGMT_FT_FILS_SHA384: + return 0; + case WPA_KEY_MGMT_DPP: + return pmk_len / 2; + case WPA_KEY_MGMT_OWE: + return pmk_len / 2; + default: + return 16; + } +} + + +/** + * wpa_use_akm_defined - Is AKM-defined Key Descriptor Version used + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: 1 if AKM-defined Key Descriptor Version is used; 0 otherwise + */ +int wpa_use_akm_defined(int akmp) +{ + return akmp == WPA_KEY_MGMT_OSEN || + akmp == WPA_KEY_MGMT_OWE || + akmp == WPA_KEY_MGMT_DPP || + akmp == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 || + wpa_key_mgmt_sae(akmp) || + wpa_key_mgmt_suite_b(akmp) || + wpa_key_mgmt_fils(akmp); +} + + +/** + * wpa_use_cmac - Is CMAC integrity algorithm used for EAPOL-Key MIC + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: 1 if CMAC is used; 0 otherwise + */ +int wpa_use_cmac(int akmp) +{ + return akmp == WPA_KEY_MGMT_OSEN || + akmp == WPA_KEY_MGMT_OWE || + akmp == WPA_KEY_MGMT_DPP || + wpa_key_mgmt_ft(akmp) || + wpa_key_mgmt_sha256(akmp) || + wpa_key_mgmt_sae(akmp) || + wpa_key_mgmt_suite_b(akmp); +} + + +/** + * wpa_use_aes_key_wrap - Is AES Keywrap algorithm used for EAPOL-Key Key Data + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: 1 if AES Keywrap is used; 0 otherwise + * + * Note: AKM 00-0F-AC:1 and 00-0F-AC:2 have special rules for selecting whether + * to use AES Keywrap based on the negotiated pairwise cipher. This function + * does not cover those special cases. + */ +int wpa_use_aes_key_wrap(int akmp) +{ + return akmp == WPA_KEY_MGMT_OSEN || + akmp == WPA_KEY_MGMT_OWE || + akmp == WPA_KEY_MGMT_DPP || + wpa_key_mgmt_ft(akmp) || + wpa_key_mgmt_sha256(akmp) || + wpa_key_mgmt_sae(akmp) || + wpa_key_mgmt_suite_b(akmp); } @@ -67,30 +192,50 @@ unsigned int wpa_mic_len(int akmp) int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, const u8 *buf, size_t len, u8 *mic) { - u8 hash[SHA384_MAC_LEN]; + u8 hash[SHA512_MAC_LEN]; + + if (key_len == 0) { + wpa_printf(MSG_DEBUG, + "WPA: KCK not set - cannot calculate MIC"); + return -1; + } switch (ver) { #ifndef CONFIG_FIPS case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4: + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using HMAC-MD5"); return hmac_md5(key, key_len, buf, len, mic); #endif /* CONFIG_FIPS */ case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES: + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using HMAC-SHA1"); if (hmac_sha1(key, key_len, buf, len, hash)) return -1; os_memcpy(mic, hash, MD5_MAC_LEN); break; #if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) case WPA_KEY_INFO_TYPE_AES_128_CMAC: + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using AES-CMAC"); return omac1_aes_128(key, buf, len, mic); #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ case WPA_KEY_INFO_TYPE_AKM_DEFINED: switch (akmp) { +#ifdef CONFIG_SAE + case WPA_KEY_MGMT_SAE: + case WPA_KEY_MGMT_FT_SAE: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - SAE)"); + return omac1_aes_128(key, buf, len, mic); +#endif /* CONFIG_SAE */ #ifdef CONFIG_HS20 case WPA_KEY_MGMT_OSEN: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - OSEN)"); return omac1_aes_128(key, buf, len, mic); #endif /* CONFIG_HS20 */ #ifdef CONFIG_SUITEB case WPA_KEY_MGMT_IEEE8021X_SUITE_B: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC using HMAC-SHA256 (AKM-defined - Suite B)"); if (hmac_sha256(key, key_len, buf, len, hash)) return -1; os_memcpy(mic, hash, MD5_MAC_LEN); @@ -98,16 +243,79 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, #endif /* CONFIG_SUITEB */ #ifdef CONFIG_SUITEB192 case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC using HMAC-SHA384 (AKM-defined - Suite B 192-bit)"); if (hmac_sha384(key, key_len, buf, len, hash)) return -1; os_memcpy(mic, hash, 24); break; #endif /* CONFIG_SUITEB192 */ +#ifdef CONFIG_OWE + case WPA_KEY_MGMT_OWE: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - OWE)", + (unsigned int) key_len * 8 * 2); + if (key_len == 128 / 8) { + if (hmac_sha256(key, key_len, buf, len, hash)) + return -1; + } else if (key_len == 192 / 8) { + if (hmac_sha384(key, key_len, buf, len, hash)) + return -1; + } else if (key_len == 256 / 8) { + if (hmac_sha512(key, key_len, buf, len, hash)) + return -1; + } else { + wpa_printf(MSG_INFO, + "OWE: Unsupported KCK length: %u", + (unsigned int) key_len); + return -1; + } + os_memcpy(mic, hash, key_len); + break; +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + case WPA_KEY_MGMT_DPP: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - DPP)", + (unsigned int) key_len * 8 * 2); + if (key_len == 128 / 8) { + if (hmac_sha256(key, key_len, buf, len, hash)) + return -1; + } else if (key_len == 192 / 8) { + if (hmac_sha384(key, key_len, buf, len, hash)) + return -1; + } else if (key_len == 256 / 8) { + if (hmac_sha512(key, key_len, buf, len, hash)) + return -1; + } else { + wpa_printf(MSG_INFO, + "DPP: Unsupported KCK length: %u", + (unsigned int) key_len); + return -1; + } + os_memcpy(mic, hash, key_len); + break; +#endif /* CONFIG_DPP */ +#if defined(CONFIG_IEEE80211R) && defined(CONFIG_SHA384) + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC using HMAC-SHA384 (AKM-defined - FT 802.1X SHA384)"); + if (hmac_sha384(key, key_len, buf, len, hash)) + return -1; + os_memcpy(mic, hash, 24); + break; +#endif /* CONFIG_IEEE80211R && CONFIG_SHA384 */ default: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC algorithm not known (AKM-defined - akmp=0x%x)", + akmp); return -1; } break; default: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC algorithm not known (ver=%d)", + ver); return -1; } @@ -133,10 +341,6 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, * PTK = PRF-X(PMK, "Pairwise key expansion", * Min(AA, SA) || Max(AA, SA) || * Min(ANonce, SNonce) || Max(ANonce, SNonce)) - * - * STK = PRF-X(SMK, "Peer key expansion", - * Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) || - * Min(INonce, PNonce) || Max(INonce, PNonce)) */ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, const u8 *addr1, const u8 *addr2, @@ -147,6 +351,11 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN]; size_t ptk_len; + if (pmk_len == 0) { + wpa_printf(MSG_ERROR, "WPA: No PMK set for PTK derivation"); + return -1; + } + if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) { os_memcpy(data, addr1, ETH_ALEN); os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN); @@ -165,24 +374,62 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, WPA_NONCE_LEN); } - ptk->kck_len = wpa_kck_len(akmp); - ptk->kek_len = wpa_kek_len(akmp); + ptk->kck_len = wpa_kck_len(akmp, pmk_len); + ptk->kek_len = wpa_kek_len(akmp, pmk_len); ptk->tk_len = wpa_cipher_key_len(cipher); + if (ptk->tk_len == 0) { + wpa_printf(MSG_ERROR, + "WPA: Unsupported cipher (0x%x) used in PTK derivation", + cipher); + return -1; + } ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len; -#ifdef CONFIG_SUITEB192 - if (wpa_key_mgmt_sha384(akmp)) - sha384_prf(pmk, pmk_len, label, data, sizeof(data), - tmp, ptk_len); - else -#endif /* CONFIG_SUITEB192 */ -#ifdef CONFIG_IEEE80211W - if (wpa_key_mgmt_sha256(akmp)) - sha256_prf(pmk, pmk_len, label, data, sizeof(data), - tmp, ptk_len); - else -#endif /* CONFIG_IEEE80211W */ - sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp, ptk_len); + if (wpa_key_mgmt_sha384(akmp)) { +#if defined(CONFIG_SUITEB192) || defined(CONFIG_FILS) + wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)"); + if (sha384_prf(pmk, pmk_len, label, data, sizeof(data), + tmp, ptk_len) < 0) + return -1; +#else /* CONFIG_SUITEB192 || CONFIG_FILS */ + return -1; +#endif /* CONFIG_SUITEB192 || CONFIG_FILS */ + } else if (wpa_key_mgmt_sha256(akmp) || akmp == WPA_KEY_MGMT_OWE) { +#if defined(CONFIG_IEEE80211W) || defined(CONFIG_SAE) || defined(CONFIG_FILS) + wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)"); + if (sha256_prf(pmk, pmk_len, label, data, sizeof(data), + tmp, ptk_len) < 0) + return -1; +#else /* CONFIG_IEEE80211W or CONFIG_SAE or CONFIG_FILS */ + return -1; +#endif /* CONFIG_IEEE80211W or CONFIG_SAE or CONFIG_FILS */ +#ifdef CONFIG_DPP + } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 32) { + wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)"); + if (sha256_prf(pmk, pmk_len, label, data, sizeof(data), + tmp, ptk_len) < 0) + return -1; + } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 48) { + wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)"); + if (sha384_prf(pmk, pmk_len, label, data, sizeof(data), + tmp, ptk_len) < 0) + return -1; + } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 64) { + wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)"); + if (sha512_prf(pmk, pmk_len, label, data, sizeof(data), + tmp, ptk_len) < 0) + return -1; + } else if (akmp == WPA_KEY_MGMT_DPP) { + wpa_printf(MSG_INFO, "DPP: Unknown PMK length %u", + (unsigned int) pmk_len); + return -1; +#endif /* CONFIG_DPP */ + } else { + wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA1)"); + if (sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp, + ptk_len) < 0) + return -1; + } wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2)); @@ -200,10 +447,290 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len); wpa_hexdump_key(MSG_DEBUG, "WPA: TK", ptk->tk, ptk->tk_len); + ptk->kek2_len = 0; + ptk->kck2_len = 0; + os_memset(tmp, 0, sizeof(tmp)); return 0; } +#ifdef CONFIG_FILS + +int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len, + const u8 *snonce, const u8 *anonce, const u8 *dh_ss, + size_t dh_ss_len, u8 *pmk, size_t *pmk_len) +{ + u8 nonces[2 * FILS_NONCE_LEN]; + const u8 *addr[2]; + size_t len[2]; + size_t num_elem; + int res; + + /* PMK = HMAC-Hash(SNonce || ANonce, rMSK [ || DHss ]) */ + wpa_printf(MSG_DEBUG, "FILS: rMSK to PMK derivation"); + + if (wpa_key_mgmt_sha384(akmp)) + *pmk_len = SHA384_MAC_LEN; + else if (wpa_key_mgmt_sha256(akmp)) + *pmk_len = SHA256_MAC_LEN; + else + return -1; + + wpa_hexdump_key(MSG_DEBUG, "FILS: rMSK", rmsk, rmsk_len); + wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: DHss", dh_ss, dh_ss_len); + + os_memcpy(nonces, snonce, FILS_NONCE_LEN); + os_memcpy(&nonces[FILS_NONCE_LEN], anonce, FILS_NONCE_LEN); + addr[0] = rmsk; + len[0] = rmsk_len; + num_elem = 1; + if (dh_ss) { + addr[1] = dh_ss; + len[1] = dh_ss_len; + num_elem++; + } + if (wpa_key_mgmt_sha384(akmp)) + res = hmac_sha384_vector(nonces, 2 * FILS_NONCE_LEN, num_elem, + addr, len, pmk); + else + res = hmac_sha256_vector(nonces, 2 * FILS_NONCE_LEN, num_elem, + addr, len, pmk); + if (res == 0) + wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, *pmk_len); + else + *pmk_len = 0; + return res; +} + + +int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len, + u8 *pmkid) +{ + const u8 *addr[1]; + size_t len[1]; + u8 hash[SHA384_MAC_LEN]; + int res; + + /* PMKID = Truncate-128(Hash(EAP-Initiate/Reauth)) */ + addr[0] = reauth; + len[0] = reauth_len; + if (wpa_key_mgmt_sha384(akmp)) + res = sha384_vector(1, addr, len, hash); + else if (wpa_key_mgmt_sha256(akmp)) + res = sha256_vector(1, addr, len, hash); + else + return -1; + if (res) + return res; + os_memcpy(pmkid, hash, PMKID_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN); + return 0; +} + + +int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa, + const u8 *snonce, const u8 *anonce, const u8 *dhss, + size_t dhss_len, struct wpa_ptk *ptk, + u8 *ick, size_t *ick_len, int akmp, int cipher, + u8 *fils_ft, size_t *fils_ft_len) +{ + u8 *data, *pos; + size_t data_len; + u8 tmp[FILS_ICK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN + + FILS_FT_MAX_LEN]; + size_t key_data_len; + const char *label = "FILS PTK Derivation"; + int ret = -1; + + /* + * FILS-Key-Data = PRF-X(PMK, "FILS PTK Derivation", + * SPA || AA || SNonce || ANonce [ || DHss ]) + * ICK = L(FILS-Key-Data, 0, ICK_bits) + * KEK = L(FILS-Key-Data, ICK_bits, KEK_bits) + * TK = L(FILS-Key-Data, ICK_bits + KEK_bits, TK_bits) + * If doing FT initial mobility domain association: + * FILS-FT = L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits, + * FILS-FT_bits) + */ + data_len = 2 * ETH_ALEN + 2 * FILS_NONCE_LEN + dhss_len; + data = os_malloc(data_len); + if (!data) + goto err; + pos = data; + os_memcpy(pos, spa, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, aa, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, snonce, FILS_NONCE_LEN); + pos += FILS_NONCE_LEN; + os_memcpy(pos, anonce, FILS_NONCE_LEN); + pos += FILS_NONCE_LEN; + if (dhss) + os_memcpy(pos, dhss, dhss_len); + + ptk->kck_len = 0; + ptk->kek_len = wpa_kek_len(akmp, pmk_len); + ptk->tk_len = wpa_cipher_key_len(cipher); + if (wpa_key_mgmt_sha384(akmp)) + *ick_len = 48; + else if (wpa_key_mgmt_sha256(akmp)) + *ick_len = 32; + else + goto err; + key_data_len = *ick_len + ptk->kek_len + ptk->tk_len; + + if (fils_ft && fils_ft_len) { + if (akmp == WPA_KEY_MGMT_FT_FILS_SHA256) { + *fils_ft_len = 32; + } else if (akmp == WPA_KEY_MGMT_FT_FILS_SHA384) { + *fils_ft_len = 48; + } else { + *fils_ft_len = 0; + fils_ft = NULL; + } + key_data_len += *fils_ft_len; + } + + if (wpa_key_mgmt_sha384(akmp)) { + wpa_printf(MSG_DEBUG, "FILS: PTK derivation using PRF(SHA384)"); + if (sha384_prf(pmk, pmk_len, label, data, data_len, + tmp, key_data_len) < 0) + goto err; + } else { + wpa_printf(MSG_DEBUG, "FILS: PTK derivation using PRF(SHA256)"); + if (sha256_prf(pmk, pmk_len, label, data, data_len, + tmp, key_data_len) < 0) + goto err; + } + + wpa_printf(MSG_DEBUG, "FILS: PTK derivation - SPA=" MACSTR + " AA=" MACSTR, MAC2STR(spa), MAC2STR(aa)); + wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN); + if (dhss) + wpa_hexdump_key(MSG_DEBUG, "FILS: DHss", dhss, dhss_len); + wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, pmk_len); + wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-Key-Data", tmp, key_data_len); + + os_memcpy(ick, tmp, *ick_len); + wpa_hexdump_key(MSG_DEBUG, "FILS: ICK", ick, *ick_len); + + os_memcpy(ptk->kek, tmp + *ick_len, ptk->kek_len); + wpa_hexdump_key(MSG_DEBUG, "FILS: KEK", ptk->kek, ptk->kek_len); + + os_memcpy(ptk->tk, tmp + *ick_len + ptk->kek_len, ptk->tk_len); + wpa_hexdump_key(MSG_DEBUG, "FILS: TK", ptk->tk, ptk->tk_len); + + if (fils_ft && fils_ft_len) { + os_memcpy(fils_ft, tmp + *ick_len + ptk->kek_len + ptk->tk_len, + *fils_ft_len); + wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-FT", + fils_ft, *fils_ft_len); + } + + ptk->kek2_len = 0; + ptk->kck2_len = 0; + + os_memset(tmp, 0, sizeof(tmp)); + ret = 0; +err: + bin_clear_free(data, data_len); + return ret; +} + + +int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce, + const u8 *anonce, const u8 *sta_addr, const u8 *bssid, + const u8 *g_sta, size_t g_sta_len, + const u8 *g_ap, size_t g_ap_len, + int akmp, u8 *key_auth_sta, u8 *key_auth_ap, + size_t *key_auth_len) +{ + const u8 *addr[6]; + size_t len[6]; + size_t num_elem = 4; + int res; + + wpa_printf(MSG_DEBUG, "FILS: Key-Auth derivation: STA-MAC=" MACSTR + " AP-BSSID=" MACSTR, MAC2STR(sta_addr), MAC2STR(bssid)); + wpa_hexdump_key(MSG_DEBUG, "FILS: ICK", ick, ick_len); + wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: gSTA", g_sta, g_sta_len); + wpa_hexdump(MSG_DEBUG, "FILS: gAP", g_ap, g_ap_len); + + /* + * For (Re)Association Request frame (STA->AP): + * Key-Auth = HMAC-Hash(ICK, SNonce || ANonce || STA-MAC || AP-BSSID + * [ || gSTA || gAP ]) + */ + addr[0] = snonce; + len[0] = FILS_NONCE_LEN; + addr[1] = anonce; + len[1] = FILS_NONCE_LEN; + addr[2] = sta_addr; + len[2] = ETH_ALEN; + addr[3] = bssid; + len[3] = ETH_ALEN; + if (g_sta && g_ap_len && g_ap && g_ap_len) { + addr[4] = g_sta; + len[4] = g_sta_len; + addr[5] = g_ap; + len[5] = g_ap_len; + num_elem = 6; + } + + if (wpa_key_mgmt_sha384(akmp)) { + *key_auth_len = 48; + res = hmac_sha384_vector(ick, ick_len, num_elem, addr, len, + key_auth_sta); + } else if (wpa_key_mgmt_sha256(akmp)) { + *key_auth_len = 32; + res = hmac_sha256_vector(ick, ick_len, num_elem, addr, len, + key_auth_sta); + } else { + return -1; + } + if (res < 0) + return res; + + /* + * For (Re)Association Response frame (AP->STA): + * Key-Auth = HMAC-Hash(ICK, ANonce || SNonce || AP-BSSID || STA-MAC + * [ || gAP || gSTA ]) + */ + addr[0] = anonce; + addr[1] = snonce; + addr[2] = bssid; + addr[3] = sta_addr; + if (g_sta && g_ap_len && g_ap && g_ap_len) { + addr[4] = g_ap; + len[4] = g_ap_len; + addr[5] = g_sta; + len[5] = g_sta_len; + } + + if (wpa_key_mgmt_sha384(akmp)) + res = hmac_sha384_vector(ick, ick_len, num_elem, addr, len, + key_auth_ap); + else if (wpa_key_mgmt_sha256(akmp)) + res = hmac_sha256_vector(ick, ick_len, num_elem, addr, len, + key_auth_ap); + if (res < 0) + return res; + + wpa_hexdump(MSG_DEBUG, "FILS: Key-Auth (STA)", + key_auth_sta, *key_auth_len); + wpa_hexdump(MSG_DEBUG, "FILS: Key-Auth (AP)", + key_auth_ap, *key_auth_len); + + return 0; +} + +#endif /* CONFIG_FILS */ + #ifdef CONFIG_IEEE80211R int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, @@ -216,14 +743,23 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, const u8 *addr[9]; size_t len[9]; size_t i, num_elem = 0; - u8 zero_mic[16]; + u8 zero_mic[24]; + size_t mic_len, fte_fixed_len; - if (kck_len != 16) { + if (kck_len == 16) { + mic_len = 16; +#ifdef CONFIG_SHA384 + } else if (kck_len == 24) { + mic_len = 24; +#endif /* CONFIG_SHA384 */ + } else { wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u", (unsigned int) kck_len); return -1; } + fte_fixed_len = sizeof(struct rsn_ftie) - 16 + mic_len; + addr[num_elem] = sta_addr; len[num_elem] = ETH_ALEN; num_elem++; @@ -247,7 +783,7 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, num_elem++; } if (ftie) { - if (ftie_len < 2 + sizeof(struct rsn_ftie)) + if (ftie_len < 2 + fte_fixed_len) return -1; /* IE hdr and mic_control */ @@ -256,14 +792,14 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, num_elem++; /* MIC field with all zeros */ - os_memset(zero_mic, 0, sizeof(zero_mic)); + os_memset(zero_mic, 0, mic_len); addr[num_elem] = zero_mic; - len[num_elem] = sizeof(zero_mic); + len[num_elem] = mic_len; num_elem++; /* Rest of FTIE */ - addr[num_elem] = ftie + 2 + 2 + 16; - len[num_elem] = ftie_len - (2 + 2 + 16); + addr[num_elem] = ftie + 2 + 2 + mic_len; + len[num_elem] = ftie_len - (2 + 2 + mic_len); num_elem++; } if (ric) { @@ -274,7 +810,17 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, for (i = 0; i < num_elem; i++) wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]); - if (omac1_aes_128_vector(kck, num_elem, addr, len, mic)) +#ifdef CONFIG_SHA384 + if (kck_len == 24) { + u8 hash[SHA384_MAC_LEN]; + + if (hmac_sha384_vector(kck, kck_len, num_elem, addr, len, hash)) + return -1; + os_memcpy(mic, hash, 24); + } +#endif /* CONFIG_SHA384 */ + if (kck_len == 16 && + omac1_aes_128_vector(kck, num_elem, addr, len, mic)) return -1; return 0; @@ -282,23 +828,27 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, - struct wpa_ft_ies *parse) + struct wpa_ft_ies *parse, int use_sha384) { const u8 *end, *pos; parse->ftie = ie; parse->ftie_len = ie_len; - pos = ie + sizeof(struct rsn_ftie); + pos = ie + (use_sha384 ? sizeof(struct rsn_ftie_sha384) : + sizeof(struct rsn_ftie)); end = ie + ie_len; + wpa_hexdump(MSG_DEBUG, "FT: Parse FTE subelements", pos, end - pos); while (end - pos >= 2) { u8 id, len; id = *pos++; len = *pos++; - if (len > end - pos) + if (len > end - pos) { + wpa_printf(MSG_DEBUG, "FT: Truncated subelement"); break; + } switch (id) { case FTIE_SUBELEM_R1KH_ID: @@ -330,6 +880,9 @@ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, parse->igtk_len = len; break; #endif /* CONFIG_IEEE80211W */ + default: + wpa_printf(MSG_DEBUG, "FT: Unknown subelem id %u", id); + break; } pos += len; @@ -340,13 +893,19 @@ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, - struct wpa_ft_ies *parse) + struct wpa_ft_ies *parse, int use_sha384) { const u8 *end, *pos; struct wpa_ie_data data; int ret; const struct rsn_ftie *ftie; int prot_ie_count = 0; + int update_use_sha384 = 0; + + if (use_sha384 < 0) { + use_sha384 = 0; + update_use_sha384 = 1; + } os_memset(parse, 0, sizeof(*parse)); if (ies == NULL) @@ -364,6 +923,7 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, switch (id) { case WLAN_EID_RSN: + wpa_hexdump(MSG_DEBUG, "FT: RSNE", pos, len); parse->rsn = pos; parse->rsn_len = len; ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2, @@ -376,22 +936,65 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, } if (data.num_pmkid == 1 && data.pmkid) parse->rsn_pmkid = data.pmkid; + parse->key_mgmt = data.key_mgmt; + parse->pairwise_cipher = data.pairwise_cipher; + if (update_use_sha384) { + use_sha384 = + wpa_key_mgmt_sha384(parse->key_mgmt); + update_use_sha384 = 0; + } break; case WLAN_EID_MOBILITY_DOMAIN: + wpa_hexdump(MSG_DEBUG, "FT: MDE", pos, len); if (len < sizeof(struct rsn_mdie)) return -1; parse->mdie = pos; parse->mdie_len = len; break; case WLAN_EID_FAST_BSS_TRANSITION: + wpa_hexdump(MSG_DEBUG, "FT: FTE", pos, len); + if (use_sha384) { + const struct rsn_ftie_sha384 *ftie_sha384; + + if (len < sizeof(*ftie_sha384)) + return -1; + ftie_sha384 = + (const struct rsn_ftie_sha384 *) pos; + wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control", + ftie_sha384->mic_control, 2); + wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC", + ftie_sha384->mic, + sizeof(ftie_sha384->mic)); + wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce", + ftie_sha384->anonce, + WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce", + ftie_sha384->snonce, + WPA_NONCE_LEN); + prot_ie_count = ftie_sha384->mic_control[1]; + if (wpa_ft_parse_ftie(pos, len, parse, 1) < 0) + return -1; + break; + } + if (len < sizeof(*ftie)) return -1; ftie = (const struct rsn_ftie *) pos; + wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control", + ftie->mic_control, 2); + wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC", + ftie->mic, sizeof(ftie->mic)); + wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce", + ftie->anonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce", + ftie->snonce, WPA_NONCE_LEN); prot_ie_count = ftie->mic_control[1]; - if (wpa_ft_parse_ftie(pos, len, parse) < 0) + if (wpa_ft_parse_ftie(pos, len, parse, 0) < 0) return -1; break; case WLAN_EID_TIMEOUT_INTERVAL: + wpa_hexdump(MSG_DEBUG, "FT: Timeout Interval", + pos, len); if (len != 5) break; parse->tie = pos; @@ -493,6 +1096,10 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s) return WPA_KEY_MGMT_FT_IEEE8021X; if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK) return WPA_KEY_MGMT_FT_PSK; +#ifdef CONFIG_SHA384 + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384) + return WPA_KEY_MGMT_FT_IEEE8021X_SHA384; +#endif /* CONFIG_SHA384 */ #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256) @@ -510,6 +1117,22 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s) return WPA_KEY_MGMT_IEEE8021X_SUITE_B; if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192) return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FILS_SHA256) + return WPA_KEY_MGMT_FILS_SHA256; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FILS_SHA384) + return WPA_KEY_MGMT_FILS_SHA384; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA256) + return WPA_KEY_MGMT_FT_FILS_SHA256; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA384) + return WPA_KEY_MGMT_FT_FILS_SHA384; +#ifdef CONFIG_OWE + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OWE) + return WPA_KEY_MGMT_OWE; +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_DPP) + return WPA_KEY_MGMT_DPP; +#endif /* CONFIG_DPP */ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN) return WPA_KEY_MGMT_OSEN; return 0; @@ -579,6 +1202,8 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, pos = rsn_ie + 6; left = rsn_ie_len - 6; + data->group_cipher = WPA_CIPHER_GTK_NOT_USED; + data->key_mgmt = WPA_KEY_MGMT_OSEN; data->proto = WPA_PROTO_OSEN; } else { const struct rsn_ie_hdr *hdr; @@ -849,27 +1474,39 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, * * IEEE Std 802.11r-2008 - 8.5.1.5.3 */ -void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, - const u8 *ssid, size_t ssid_len, - const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, - const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name) +int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, + const u8 *ssid, size_t ssid_len, + const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, + const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name, + int use_sha384) { u8 buf[1 + SSID_MAX_LEN + MOBILITY_DOMAIN_ID_LEN + 1 + FT_R0KH_ID_MAX_LEN + ETH_ALEN]; - u8 *pos, r0_key_data[48], hash[32]; + u8 *pos, r0_key_data[64], hash[48]; const u8 *addr[2]; size_t len[2]; + size_t q = use_sha384 ? 48 : 32; + size_t r0_key_data_len = q + 16; /* * R0-Key-Data = KDF-384(XXKey, "FT-R0", * SSIDlength || SSID || MDID || R0KHlength || * R0KH-ID || S0KH-ID) - * XXKey is either the second 256 bits of MSK or PSK. - * PMK-R0 = L(R0-Key-Data, 0, 256) - * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128) + * XXKey is either the second 256 bits of MSK or PSK; or the first + * 384 bits of MSK for FT-EAP-SHA384. + * PMK-R0 = L(R0-Key-Data, 0, Q) + * PMK-R0Name-Salt = L(R0-Key-Data, Q, 128) + * Q = 384 for FT-EAP-SHA384; otherwise, 256 */ if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN) - return; + return -1; + wpa_printf(MSG_DEBUG, "FT: Derive PMK-R0 using KDF-%s", + use_sha384 ? "SHA384" : "SHA256"); + wpa_hexdump_key(MSG_DEBUG, "FT: XXKey", xxkey, xxkey_len); + wpa_hexdump_ascii(MSG_DEBUG, "FT: SSID", ssid, ssid_len); + wpa_hexdump(MSG_DEBUG, "FT: MDID", mdid, MOBILITY_DOMAIN_ID_LEN); + wpa_hexdump_ascii(MSG_DEBUG, "FT: R0KH-ID", r0kh_id, r0kh_id_len); + wpa_printf(MSG_DEBUG, "FT: S0KH-ID: " MACSTR, MAC2STR(s0kh_id)); pos = buf; *pos++ = ssid_len; os_memcpy(pos, ssid, ssid_len); @@ -882,20 +1519,51 @@ void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, os_memcpy(pos, s0kh_id, ETH_ALEN); pos += ETH_ALEN; - sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf, - r0_key_data, sizeof(r0_key_data)); - os_memcpy(pmk_r0, r0_key_data, PMK_LEN); +#ifdef CONFIG_SHA384 + if (use_sha384) { + if (xxkey_len != SHA384_MAC_LEN) { + wpa_printf(MSG_ERROR, + "FT: Unexpected XXKey length %d (expected %d)", + (int) xxkey_len, SHA384_MAC_LEN); + return -1; + } + if (sha384_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf, + r0_key_data, r0_key_data_len) < 0) + return -1; + } +#endif /* CONFIG_SHA384 */ + if (!use_sha384) { + if (xxkey_len != PMK_LEN) { + wpa_printf(MSG_ERROR, + "FT: Unexpected XXKey length %d (expected %d)", + (int) xxkey_len, PMK_LEN); + return -1; + } + if (sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf, + r0_key_data, r0_key_data_len) < 0) + return -1; + } + os_memcpy(pmk_r0, r0_key_data, q); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, q); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0Name-Salt", &r0_key_data[q], 16); /* - * PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt) + * PMKR0Name = Truncate-128(Hash("FT-R0N" || PMK-R0Name-Salt) */ addr[0] = (const u8 *) "FT-R0N"; len[0] = 6; - addr[1] = r0_key_data + PMK_LEN; + addr[1] = &r0_key_data[q]; len[1] = 16; - sha256_vector(2, addr, len, hash); +#ifdef CONFIG_SHA384 + if (use_sha384 && sha384_vector(2, addr, len, hash) < 0) + return -1; +#endif /* CONFIG_SHA384 */ + if (!use_sha384 && sha256_vector(2, addr, len, hash) < 0) + return -1; os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN); + os_memset(r0_key_data, 0, sizeof(r0_key_data)); + return 0; } @@ -904,16 +1572,16 @@ void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, * * IEEE Std 802.11r-2008 - 8.5.1.5.4 */ -void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, - const u8 *s1kh_id, u8 *pmk_r1_name) +int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, + const u8 *s1kh_id, u8 *pmk_r1_name, int use_sha384) { - u8 hash[32]; + u8 hash[48]; const u8 *addr[4]; size_t len[4]; /* - * PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name || - * R1KH-ID || S1KH-ID)) + * PMKR1Name = Truncate-128(Hash("FT-R1N" || PMKR0Name || + * R1KH-ID || S1KH-ID)) */ addr[0] = (const u8 *) "FT-R1N"; len[0] = 6; @@ -924,8 +1592,14 @@ void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, addr[3] = s1kh_id; len[3] = ETH_ALEN; - sha256_vector(4, addr, len, hash); +#ifdef CONFIG_SHA384 + if (use_sha384 && sha384_vector(4, addr, len, hash) < 0) + return -1; +#endif /* CONFIG_SHA384 */ + if (!use_sha384 && sha256_vector(4, addr, len, hash) < 0) + return -1; os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN); + return 0; } @@ -934,23 +1608,46 @@ void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, * * IEEE Std 802.11r-2008 - 8.5.1.5.4 */ -void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name, - const u8 *r1kh_id, const u8 *s1kh_id, - u8 *pmk_r1, u8 *pmk_r1_name) +int wpa_derive_pmk_r1(const u8 *pmk_r0, size_t pmk_r0_len, + const u8 *pmk_r0_name, + const u8 *r1kh_id, const u8 *s1kh_id, + u8 *pmk_r1, u8 *pmk_r1_name) { u8 buf[FT_R1KH_ID_LEN + ETH_ALEN]; u8 *pos; /* PMK-R1 = KDF-256(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */ + wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 using KDF-%s", + pmk_r0_len == SHA384_MAC_LEN ? "SHA384" : "SHA256"); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len); + wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", r1kh_id, FT_R1KH_ID_LEN); + wpa_printf(MSG_DEBUG, "FT: S1KH-ID: " MACSTR, MAC2STR(s1kh_id)); pos = buf; os_memcpy(pos, r1kh_id, FT_R1KH_ID_LEN); pos += FT_R1KH_ID_LEN; os_memcpy(pos, s1kh_id, ETH_ALEN); pos += ETH_ALEN; - sha256_prf(pmk_r0, PMK_LEN, "FT-R1", buf, pos - buf, pmk_r1, PMK_LEN); +#ifdef CONFIG_SHA384 + if (pmk_r0_len == SHA384_MAC_LEN && + sha384_prf(pmk_r0, pmk_r0_len, "FT-R1", + buf, pos - buf, pmk_r1, pmk_r0_len) < 0) + return -1; +#endif /* CONFIG_SHA384 */ + if (pmk_r0_len == PMK_LEN && + sha256_prf(pmk_r0, pmk_r0_len, "FT-R1", + buf, pos - buf, pmk_r1, pmk_r0_len) < 0) + return -1; + if (pmk_r0_len != SHA384_MAC_LEN && pmk_r0_len != PMK_LEN) { + wpa_printf(MSG_ERROR, "FT: Unexpected PMK-R0 length %d", + (int) pmk_r0_len); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r0_len); - wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id, pmk_r1_name); + return wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id, + pmk_r1_name, + pmk_r0_len == SHA384_MAC_LEN); } @@ -959,7 +1656,8 @@ void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name, * * IEEE Std 802.11r-2008 - 8.5.1.5.5 */ -int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, +int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, + const u8 *snonce, const u8 *anonce, const u8 *sta_addr, const u8 *bssid, const u8 *pmk_r1_name, struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher) @@ -968,13 +1666,21 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, u8 *pos, hash[32]; const u8 *addr[6]; size_t len[6]; - u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN]; - size_t ptk_len; + u8 tmp[2 * WPA_KCK_MAX_LEN + 2 * WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN]; + size_t ptk_len, offset; + int use_sha384 = wpa_key_mgmt_sha384(akmp); /* * PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce || * BSSID || STA-ADDR) */ + wpa_printf(MSG_DEBUG, "FT: Derive PTK using KDF-%s", + use_sha384 ? "SHA384" : "SHA256"); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len); + wpa_hexdump(MSG_DEBUG, "FT: SNonce", snonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: ANonce", anonce, WPA_NONCE_LEN); + wpa_printf(MSG_DEBUG, "FT: BSSID=" MACSTR " STA-ADDR=" MACSTR, + MAC2STR(bssid), MAC2STR(sta_addr)); pos = buf; os_memcpy(pos, snonce, WPA_NONCE_LEN); pos += WPA_NONCE_LEN; @@ -985,17 +1691,45 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, os_memcpy(pos, sta_addr, ETH_ALEN); pos += ETH_ALEN; - ptk->kck_len = wpa_kck_len(akmp); - ptk->kek_len = wpa_kek_len(akmp); + ptk->kck_len = wpa_kck_len(akmp, PMK_LEN); + ptk->kck2_len = wpa_kck2_len(akmp); + ptk->kek_len = wpa_kek_len(akmp, PMK_LEN); + ptk->kek2_len = wpa_kek2_len(akmp); ptk->tk_len = wpa_cipher_key_len(cipher); - ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len; + ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len + + ptk->kck2_len + ptk->kek2_len; - sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, tmp, ptk_len); +#ifdef CONFIG_SHA384 + if (use_sha384) { + if (pmk_r1_len != SHA384_MAC_LEN) { + wpa_printf(MSG_ERROR, + "FT: Unexpected PMK-R1 length %d (expected %d)", + (int) pmk_r1_len, SHA384_MAC_LEN); + return -1; + } + if (sha384_prf(pmk_r1, pmk_r1_len, "FT-PTK", + buf, pos - buf, tmp, ptk_len) < 0) + return -1; + } +#endif /* CONFIG_SHA384 */ + if (!use_sha384) { + if (pmk_r1_len != PMK_LEN) { + wpa_printf(MSG_ERROR, + "FT: Unexpected PMK-R1 length %d (expected %d)", + (int) pmk_r1_len, PMK_LEN); + return -1; + } + if (sha256_prf(pmk_r1, pmk_r1_len, "FT-PTK", + buf, pos - buf, tmp, ptk_len) < 0) + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "FT: PTK", tmp, ptk_len); /* * PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce || * ANonce || BSSID || STA-ADDR)) */ + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); addr[0] = pmk_r1_name; len[0] = WPA_PMK_NAME_LEN; addr[1] = (const u8 *) "FT-PTKN"; @@ -1009,15 +1743,28 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, addr[5] = sta_addr; len[5] = ETH_ALEN; - sha256_vector(6, addr, len, hash); + if (sha256_vector(6, addr, len, hash) < 0) + return -1; os_memcpy(ptk_name, hash, WPA_PMK_NAME_LEN); os_memcpy(ptk->kck, tmp, ptk->kck_len); - os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len); - os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len); + offset = ptk->kck_len; + os_memcpy(ptk->kek, tmp + offset, ptk->kek_len); + offset += ptk->kek_len; + os_memcpy(ptk->tk, tmp + offset, ptk->tk_len); + offset += ptk->tk_len; + os_memcpy(ptk->kck2, tmp + offset, ptk->kck2_len); + offset = ptk->kck2_len; + os_memcpy(ptk->kek2, tmp + offset, ptk->kek2_len); wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len); wpa_hexdump_key(MSG_DEBUG, "FT: KEK", ptk->kek, ptk->kek_len); + if (ptk->kck2_len) + wpa_hexdump_key(MSG_DEBUG, "FT: KCK2", + ptk->kck2, ptk->kck2_len); + if (ptk->kek2_len) + wpa_hexdump_key(MSG_DEBUG, "FT: KEK2", + ptk->kek2, ptk->kek2_len); wpa_hexdump_key(MSG_DEBUG, "FT: TK", ptk->tk, ptk->tk_len); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); @@ -1036,29 +1783,48 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, * @aa: Authenticator address * @spa: Supplicant address * @pmkid: Buffer for PMKID - * @use_sha256: Whether to use SHA256-based KDF + * @akmp: Negotiated key management protocol * - * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy - * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA) + * IEEE Std 802.11-2016 - 12.7.1.3 Pairwise key hierarchy + * AKM: 00-0F-AC:5, 00-0F-AC:6, 00-0F-AC:14, 00-0F-AC:16 + * PMKID = Truncate-128(HMAC-SHA-256(PMK, "PMK Name" || AA || SPA)) + * AKM: 00-0F-AC:11 + * See rsn_pmkid_suite_b() + * AKM: 00-0F-AC:12 + * See rsn_pmkid_suite_b_192() + * AKM: 00-0F-AC:13, 00-0F-AC:15, 00-0F-AC:17 + * PMKID = Truncate-128(HMAC-SHA-384(PMK, "PMK Name" || AA || SPA)) + * Otherwise: + * PMKID = Truncate-128(HMAC-SHA-1(PMK, "PMK Name" || AA || SPA)) */ void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, - u8 *pmkid, int use_sha256) + u8 *pmkid, int akmp) { char *title = "PMK Name"; const u8 *addr[3]; const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; - unsigned char hash[SHA256_MAC_LEN]; + unsigned char hash[SHA384_MAC_LEN]; addr[0] = (u8 *) title; addr[1] = aa; addr[2] = spa; -#ifdef CONFIG_IEEE80211W - if (use_sha256) + if (0) { +#if defined(CONFIG_FILS) || defined(CONFIG_SHA384) + } else if (wpa_key_mgmt_sha384(akmp)) { + wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-384"); + hmac_sha384_vector(pmk, pmk_len, 3, addr, len, hash); +#endif /* CONFIG_FILS || CONFIG_SHA384 */ +#if defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) + } else if (wpa_key_mgmt_sha256(akmp)) { + wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-256"); hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash); - else -#endif /* CONFIG_IEEE80211W */ +#endif /* CONFIG_IEEE80211W || CONFIG_FILS */ + } else { + wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-1"); hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash); + } + wpa_hexdump(MSG_DEBUG, "RSN: Derived PMKID", hash, PMKID_LEN); os_memcpy(pmkid, hash, PMKID_LEN); } @@ -1155,6 +1921,14 @@ const char * wpa_cipher_txt(int cipher) return "GCMP-256"; case WPA_CIPHER_CCMP_256: return "CCMP-256"; + case WPA_CIPHER_AES_128_CMAC: + return "BIP"; + case WPA_CIPHER_BIP_GMAC_128: + return "BIP-GMAC-128"; + case WPA_CIPHER_BIP_GMAC_256: + return "BIP-GMAC-256"; + case WPA_CIPHER_BIP_CMAC_256: + return "BIP-CMAC-256"; case WPA_CIPHER_GTK_NOT_USED: return "GTK_NOT_USED"; default: @@ -1191,6 +1965,8 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto) #ifdef CONFIG_IEEE80211R case WPA_KEY_MGMT_FT_IEEE8021X: return "FT-EAP"; + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + return "FT-EAP-SHA384"; case WPA_KEY_MGMT_FT_PSK: return "FT-PSK"; #endif /* CONFIG_IEEE80211R */ @@ -1212,6 +1988,18 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto) return "WPA2-EAP-SUITE-B"; case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: return "WPA2-EAP-SUITE-B-192"; + case WPA_KEY_MGMT_FILS_SHA256: + return "FILS-SHA256"; + case WPA_KEY_MGMT_FILS_SHA384: + return "FILS-SHA384"; + case WPA_KEY_MGMT_FT_FILS_SHA256: + return "FT-FILS-SHA256"; + case WPA_KEY_MGMT_FT_FILS_SHA384: + return "FT-FILS-SHA384"; + case WPA_KEY_MGMT_OWE: + return "OWE"; + case WPA_KEY_MGMT_DPP: + return "DPP"; default: return "UNKNOWN"; } @@ -1220,28 +2008,36 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto) u32 wpa_akm_to_suite(int akm) { + if (akm & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) + return RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384; if (akm & WPA_KEY_MGMT_FT_IEEE8021X) - return WLAN_AKM_SUITE_FT_8021X; + return RSN_AUTH_KEY_MGMT_FT_802_1X; if (akm & WPA_KEY_MGMT_FT_PSK) - return WLAN_AKM_SUITE_FT_PSK; - if (akm & WPA_KEY_MGMT_IEEE8021X) - return WLAN_AKM_SUITE_8021X; + return RSN_AUTH_KEY_MGMT_FT_PSK; if (akm & WPA_KEY_MGMT_IEEE8021X_SHA256) - return WLAN_AKM_SUITE_8021X_SHA256; + return RSN_AUTH_KEY_MGMT_802_1X_SHA256; if (akm & WPA_KEY_MGMT_IEEE8021X) - return WLAN_AKM_SUITE_8021X; + return RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; if (akm & WPA_KEY_MGMT_PSK_SHA256) - return WLAN_AKM_SUITE_PSK_SHA256; + return RSN_AUTH_KEY_MGMT_PSK_SHA256; if (akm & WPA_KEY_MGMT_PSK) - return WLAN_AKM_SUITE_PSK; + return RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; if (akm & WPA_KEY_MGMT_CCKM) - return WLAN_AKM_SUITE_CCKM; + return RSN_AUTH_KEY_MGMT_CCKM; if (akm & WPA_KEY_MGMT_OSEN) - return WLAN_AKM_SUITE_OSEN; + return RSN_AUTH_KEY_MGMT_OSEN; if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B) - return WLAN_AKM_SUITE_8021X_SUITE_B; + return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) - return WLAN_AKM_SUITE_8021X_SUITE_B_192; + return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; + if (akm & WPA_KEY_MGMT_FILS_SHA256) + return RSN_AUTH_KEY_MGMT_FILS_SHA256; + if (akm & WPA_KEY_MGMT_FILS_SHA384) + return RSN_AUTH_KEY_MGMT_FILS_SHA384; + if (akm & WPA_KEY_MGMT_FT_FILS_SHA256) + return RSN_AUTH_KEY_MGMT_FT_FILS_SHA256; + if (akm & WPA_KEY_MGMT_FT_FILS_SHA384) + return RSN_AUTH_KEY_MGMT_FT_FILS_SHA384; return 0; } @@ -1283,7 +2079,7 @@ int wpa_compare_rsn_ie(int ft_initial_assoc, } -#ifdef CONFIG_IEEE80211R +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS) int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid) { u8 *start, *end, *rpos, *rend; @@ -1382,7 +2178,7 @@ int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid) return 0; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R || CONFIG_FILS */ int wpa_cipher_key_len(int cipher) @@ -1421,7 +2217,7 @@ int wpa_cipher_rsc_len(int cipher) } -int wpa_cipher_to_alg(int cipher) +enum wpa_alg wpa_cipher_to_alg(int cipher) { switch (cipher) { case WPA_CIPHER_CCMP_256: @@ -1616,6 +2412,14 @@ int wpa_parse_cipher(const char *value) val |= WPA_CIPHER_NONE; else if (os_strcmp(start, "GTK_NOT_USED") == 0) val |= WPA_CIPHER_GTK_NOT_USED; + else if (os_strcmp(start, "AES-128-CMAC") == 0) + val |= WPA_CIPHER_AES_128_CMAC; + else if (os_strcmp(start, "BIP-GMAC-128") == 0) + val |= WPA_CIPHER_BIP_GMAC_128; + else if (os_strcmp(start, "BIP-GMAC-256") == 0) + val |= WPA_CIPHER_BIP_GMAC_256; + else if (os_strcmp(start, "BIP-CMAC-256") == 0) + val |= WPA_CIPHER_BIP_CMAC_256; else { os_free(buf); return -1; @@ -1671,6 +2475,34 @@ int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim) return -1; pos += ret; } + if (ciphers & WPA_CIPHER_AES_128_CMAC) { + ret = os_snprintf(pos, end - pos, "%sAES-128-CMAC", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_BIP_GMAC_128) { + ret = os_snprintf(pos, end - pos, "%sBIP-GMAC-128", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_BIP_GMAC_256) { + ret = os_snprintf(pos, end - pos, "%sBIP-GMAC-256", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_BIP_CMAC_256) { + ret = os_snprintf(pos, end - pos, "%sBIP-CMAC-256", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } if (ciphers & WPA_CIPHER_NONE) { ret = os_snprintf(pos, end - pos, "%sNONE", pos == start ? "" : delim); @@ -1705,3 +2537,29 @@ int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise) return WPA_CIPHER_CCMP_256; return WPA_CIPHER_CCMP; } + + +#ifdef CONFIG_FILS +int fils_domain_name_hash(const char *domain, u8 *hash) +{ + char buf[255], *wpos = buf; + const char *pos = domain; + size_t len; + const u8 *addr[1]; + u8 mac[SHA256_MAC_LEN]; + + for (len = 0; len < sizeof(buf) && *pos; len++) { + if (isalpha(*pos) && isupper(*pos)) + *wpos++ = tolower(*pos); + else + *wpos++ = *pos; + pos++; + } + + addr[0] = (const u8 *) buf; + if (sha256_vector(1, addr, &len, mac) < 0) + return -1; + os_memcpy(hash, mac, 2); + return 0; +} +#endif /* CONFIG_FILS */ diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 1021ccb05a71..62617444058b 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -1,6 +1,6 @@ /* * WPA definitions shared between hostapd and wpa_supplicant - * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -13,13 +13,15 @@ #define PMKID_LEN 16 #define PMK_LEN 32 #define PMK_LEN_SUITE_B_192 48 -#define PMK_LEN_MAX 48 +#define PMK_LEN_MAX 64 #define WPA_REPLAY_COUNTER_LEN 8 #define WPA_NONCE_LEN 32 #define WPA_KEY_RSC_LEN 8 #define WPA_GMK_LEN 32 #define WPA_GTK_MAX_LEN 32 +#define OWE_DH_GROUP 19 + #define WPA_ALLOWED_PAIRWISE_CIPHERS \ (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \ WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256) @@ -27,6 +29,9 @@ WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256) (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | \ WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \ WPA_CIPHER_GTK_NOT_USED) +#define WPA_ALLOWED_GROUP_MGMT_CIPHERS \ +(WPA_CIPHER_AES_128_CMAC | WPA_CIPHER_BIP_GMAC_128 | WPA_CIPHER_BIP_GMAC_256 | \ +WPA_CIPHER_BIP_CMAC_256) #define WPA_SELECTOR_LEN 4 #define WPA_VERSION 1 @@ -48,10 +53,8 @@ WPA_CIPHER_GTK_NOT_USED) #define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1) #define RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 2) -#ifdef CONFIG_IEEE80211R #define RSN_AUTH_KEY_MGMT_FT_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 3) #define RSN_AUTH_KEY_MGMT_FT_PSK RSN_SELECTOR(0x00, 0x0f, 0xac, 4) -#endif /* CONFIG_IEEE80211R */ #define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) #define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #define RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7) @@ -59,17 +62,24 @@ WPA_CIPHER_GTK_NOT_USED) #define RSN_AUTH_KEY_MGMT_FT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 9) #define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B RSN_SELECTOR(0x00, 0x0f, 0xac, 11) #define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192 RSN_SELECTOR(0x00, 0x0f, 0xac, 12) -#define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_192 \ -RSN_SELECTOR(0x00, 0x0f, 0xac, 13) +#define RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 13) +#define RSN_AUTH_KEY_MGMT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 14) +#define RSN_AUTH_KEY_MGMT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 15) +#define RSN_AUTH_KEY_MGMT_FT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 16) +#define RSN_AUTH_KEY_MGMT_FT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 17) +#define RSN_AUTH_KEY_MGMT_OWE RSN_SELECTOR(0x00, 0x0f, 0xac, 18) #define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00) #define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01) +#define RSN_AUTH_KEY_MGMT_DPP RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x02) #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0) +#define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1) #define RSN_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x0f, 0xac, 2) #if 0 #define RSN_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x0f, 0xac, 3) #endif #define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4) +#define RSN_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7) #define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8) @@ -78,6 +88,12 @@ RSN_SELECTOR(0x00, 0x0f, 0xac, 13) #define RSN_CIPHER_SUITE_BIP_GMAC_128 RSN_SELECTOR(0x00, 0x0f, 0xac, 11) #define RSN_CIPHER_SUITE_BIP_GMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 12) #define RSN_CIPHER_SUITE_BIP_CMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 13) +#define RSN_CIPHER_SUITE_SMS4 RSN_SELECTOR(0x00, 0x14, 0x72, 1) +#define RSN_CIPHER_SUITE_CKIP RSN_SELECTOR(0x00, 0x40, 0x96, 0) +#define RSN_CIPHER_SUITE_CKIP_CMIC RSN_SELECTOR(0x00, 0x40, 0x96, 1) +#define RSN_CIPHER_SUITE_CMIC RSN_SELECTOR(0x00, 0x40, 0x96, 2) +/* KRK is defined for nl80211 use only */ +#define RSN_CIPHER_SUITE_KRK RSN_SELECTOR(0x00, 0x40, 0x96, 255) /* EAPOL-Key Key Data Encapsulation * GroupKey and PeerKey require encryption, otherwise, encryption is optional. @@ -88,12 +104,6 @@ RSN_SELECTOR(0x00, 0x0f, 0xac, 13) #endif #define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3) #define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4) -#ifdef CONFIG_PEERKEY -#define RSN_KEY_DATA_SMK RSN_SELECTOR(0x00, 0x0f, 0xac, 5) -#define RSN_KEY_DATA_NONCE RSN_SELECTOR(0x00, 0x0f, 0xac, 6) -#define RSN_KEY_DATA_LIFETIME RSN_SELECTOR(0x00, 0x0f, 0xac, 7) -#define RSN_KEY_DATA_ERROR RSN_SELECTOR(0x00, 0x0f, 0xac, 8) -#endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211W #define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9) #endif /* CONFIG_IEEE80211W */ @@ -179,30 +189,17 @@ struct wpa_eapol_key { u8 key_iv[16]; u8 key_rsc[WPA_KEY_RSC_LEN]; u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */ - u8 key_mic[16]; - u8 key_data_length[2]; /* big endian */ - /* followed by key_data_length bytes of key_data */ -} STRUCT_PACKED; - -struct wpa_eapol_key_192 { - u8 type; - /* Note: key_info, key_length, and key_data_length are unaligned */ - u8 key_info[2]; /* big endian */ - u8 key_length[2]; /* big endian */ - u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; - u8 key_nonce[WPA_NONCE_LEN]; - u8 key_iv[16]; - u8 key_rsc[WPA_KEY_RSC_LEN]; - u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */ - u8 key_mic[24]; - u8 key_data_length[2]; /* big endian */ - /* followed by key_data_length bytes of key_data */ + /* variable length Key MIC field */ + /* big endian 2-octet Key Data Length field */ + /* followed by Key Data Length bytes of Key Data */ } STRUCT_PACKED; -#define WPA_EAPOL_KEY_MIC_MAX_LEN 24 -#define WPA_KCK_MAX_LEN 24 -#define WPA_KEK_MAX_LEN 32 +#define WPA_EAPOL_KEY_MIC_MAX_LEN 32 +#define WPA_KCK_MAX_LEN 32 +#define WPA_KEK_MAX_LEN 64 #define WPA_TK_MAX_LEN 32 +#define FILS_ICK_MAX_LEN 48 +#define FILS_FT_MAX_LEN 48 /** * struct wpa_ptk - WPA Pairwise Transient Key @@ -212,9 +209,13 @@ struct wpa_ptk { u8 kck[WPA_KCK_MAX_LEN]; /* EAPOL-Key Key Confirmation Key (KCK) */ u8 kek[WPA_KEK_MAX_LEN]; /* EAPOL-Key Key Encryption Key (KEK) */ u8 tk[WPA_TK_MAX_LEN]; /* Temporal Key (TK) */ + u8 kck2[WPA_KCK_MAX_LEN]; /* FT reasoc Key Confirmation Key (KCK2) */ + u8 kek2[WPA_KEK_MAX_LEN]; /* FT reassoc Key Encryption Key (KEK2) */ size_t kck_len; size_t kek_len; size_t tk_len; + size_t kck2_len; + size_t kek2_len; int installed; /* 1 if key has already been installed to driver */ }; @@ -283,22 +284,6 @@ struct rsn_ie_hdr { } STRUCT_PACKED; -#ifdef CONFIG_PEERKEY -enum { - STK_MUI_4WAY_STA_AP = 1, - STK_MUI_4WAY_STAT_STA = 2, - STK_MUI_GTK = 3, - STK_MUI_SMK = 4 -}; - -enum { - STK_ERR_STA_NR = 1, - STK_ERR_STA_NRSN = 2, - STK_ERR_CPHR_NS = 3, - STK_ERR_NO_STSL = 4 -}; -#endif /* CONFIG_PEERKEY */ - struct rsn_error_kde { be16 mui; be16 error_type; @@ -329,6 +314,14 @@ struct rsn_ftie { /* followed by optional parameters */ } STRUCT_PACKED; +struct rsn_ftie_sha384 { + u8 mic_control[2]; + u8 mic[24]; + u8 anonce[WPA_NONCE_LEN]; + u8 snonce[WPA_NONCE_LEN]; + /* followed by optional parameters */ +} STRUCT_PACKED; + #define FTIE_SUBELEM_R1KH_ID 1 #define FTIE_SUBELEM_GTK 2 #define FTIE_SUBELEM_R0KH_ID 3 @@ -352,6 +345,22 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, const u8 *addr1, const u8 *addr2, const u8 *nonce1, const u8 *nonce2, struct wpa_ptk *ptk, int akmp, int cipher); +int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len, + const u8 *snonce, const u8 *anonce, const u8 *dh_ss, + size_t dh_ss_len, u8 *pmk, size_t *pmk_len); +int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len, + u8 *pmkid); +int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa, + const u8 *snonce, const u8 *anonce, const u8 *dhss, + size_t dhss_len, struct wpa_ptk *ptk, + u8 *ick, size_t *ick_len, int akmp, int cipher, + u8 *fils_ft, size_t *fils_ft_len); +int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce, + const u8 *anonce, const u8 *sta_addr, const u8 *bssid, + const u8 *g_sta, size_t g_sta_len, + const u8 *g_ap, size_t g_ap_len, + int akmp, u8 *key_auth_sta, u8 *key_auth_ap, + size_t *key_auth_len); #ifdef CONFIG_IEEE80211R int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, @@ -360,17 +369,19 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, const u8 *ftie, size_t ftie_len, const u8 *rsnie, size_t rsnie_len, const u8 *ric, size_t ric_len, u8 *mic); -void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, - const u8 *ssid, size_t ssid_len, - const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, - const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name); -void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, - const u8 *s1kh_id, u8 *pmk_r1_name); -void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name, - const u8 *r1kh_id, const u8 *s1kh_id, - u8 *pmk_r1, u8 *pmk_r1_name); -int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, - const u8 *sta_addr, const u8 *bssid, +int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, + const u8 *ssid, size_t ssid_len, + const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, + const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name, + int use_sha384); +int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, + const u8 *s1kh_id, u8 *pmk_r1_name, int use_sha384); +int wpa_derive_pmk_r1(const u8 *pmk_r0, size_t pmk_r0_len, + const u8 *pmk_r0_name, + const u8 *r1kh_id, const u8 *s1kh_id, + u8 *pmk_r1, u8 *pmk_r1_name); +int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, const u8 *snonce, + const u8 *anonce, const u8 *sta_addr, const u8 *bssid, const u8 *pmk_r1_name, struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher); #endif /* CONFIG_IEEE80211R */ @@ -393,7 +404,7 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ie_data *data); void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, - u8 *pmkid, int use_sha256); + u8 *pmkid, int akmp); #ifdef CONFIG_SUITEB int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, u8 *pmkid); @@ -442,13 +453,16 @@ struct wpa_ft_ies { size_t igtk_len; const u8 *ric; size_t ric_len; + int key_mgmt; + int pairwise_cipher; }; -int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse); +int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse, + int use_sha384); int wpa_cipher_key_len(int cipher); int wpa_cipher_rsc_len(int cipher); -int wpa_cipher_to_alg(int cipher); +enum wpa_alg wpa_cipher_to_alg(int cipher); int wpa_cipher_valid_group(int cipher); int wpa_cipher_valid_pairwise(int cipher); int wpa_cipher_valid_mgmt_group(int cipher); @@ -460,6 +474,10 @@ int wpa_pick_group_cipher(int ciphers); int wpa_parse_cipher(const char *value); int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim); int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise); -unsigned int wpa_mic_len(int akmp); +unsigned int wpa_mic_len(int akmp, size_t pmk_len); +int wpa_use_akm_defined(int akmp); +int wpa_use_cmac(int akmp); +int wpa_use_aes_key_wrap(int akmp); +int fils_domain_name_hash(const char *domain, u8 *hash); #endif /* WPA_COMMON_H */ diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 4dcba81dc1a4..f65077e04282 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -1,6 +1,6 @@ /* * wpa_supplicant/hostapd control interface library - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -50,10 +50,19 @@ extern "C" { #define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR " /** EAP status */ #define WPA_EVENT_EAP_STATUS "CTRL-EVENT-EAP-STATUS " +/** Retransmit the previous request packet */ +#define WPA_EVENT_EAP_RETRANSMIT "CTRL-EVENT-EAP-RETRANSMIT " +#define WPA_EVENT_EAP_RETRANSMIT2 "CTRL-EVENT-EAP-RETRANSMIT2 " /** EAP authentication completed successfully */ #define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS " +#define WPA_EVENT_EAP_SUCCESS2 "CTRL-EVENT-EAP-SUCCESS2 " /** EAP authentication failed (EAP-Failure received) */ #define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE " +#define WPA_EVENT_EAP_FAILURE2 "CTRL-EVENT-EAP-FAILURE2 " +/** EAP authentication failed due to no response received */ +#define WPA_EVENT_EAP_TIMEOUT_FAILURE "CTRL-EVENT-EAP-TIMEOUT-FAILURE " +#define WPA_EVENT_EAP_TIMEOUT_FAILURE2 "CTRL-EVENT-EAP-TIMEOUT-FAILURE2 " +#define WPA_EVENT_EAP_ERROR_CODE "EAP-ERROR-CODE " /** Network block temporarily disabled (e.g., due to authentication failure) */ #define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED " /** Temporarily disabled network block re-enabled */ @@ -74,10 +83,15 @@ extern "C" { #define WPA_EVENT_NETWORK_NOT_FOUND "CTRL-EVENT-NETWORK-NOT-FOUND " /** Change in the signal level was reported by the driver */ #define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE " +/** Beacon loss reported by the driver */ +#define WPA_EVENT_BEACON_LOSS "CTRL-EVENT-BEACON-LOSS " /** Regulatory domain channel */ #define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE " /** Channel switch (followed by freq=<MHz> and other channel parameters) */ #define WPA_EVENT_CHANNEL_SWITCH "CTRL-EVENT-CHANNEL-SWITCH " +/** SAE authentication failed due to unknown password identifier */ +#define WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER \ + "CTRL-EVENT-SAE-UNKNOWN-PASSWORD-IDENTIFIER " /** IP subnet status change notification * @@ -141,6 +155,33 @@ extern "C" { #define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS " #define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG " +/* DPP events */ +#define DPP_EVENT_AUTH_SUCCESS "DPP-AUTH-SUCCESS " +#define DPP_EVENT_AUTH_INIT_FAILED "DPP-AUTH-INIT-FAILED " +#define DPP_EVENT_NOT_COMPATIBLE "DPP-NOT-COMPATIBLE " +#define DPP_EVENT_RESPONSE_PENDING "DPP-RESPONSE-PENDING " +#define DPP_EVENT_SCAN_PEER_QR_CODE "DPP-SCAN-PEER-QR-CODE " +#define DPP_EVENT_AUTH_DIRECTION "DPP-AUTH-DIRECTION " +#define DPP_EVENT_CONF_RECEIVED "DPP-CONF-RECEIVED " +#define DPP_EVENT_CONF_SENT "DPP-CONF-SENT " +#define DPP_EVENT_CONF_FAILED "DPP-CONF-FAILED " +#define DPP_EVENT_CONFOBJ_AKM "DPP-CONFOBJ-AKM " +#define DPP_EVENT_CONFOBJ_SSID "DPP-CONFOBJ-SSID " +#define DPP_EVENT_CONFOBJ_PASS "DPP-CONFOBJ-PASS " +#define DPP_EVENT_CONFOBJ_PSK "DPP-CONFOBJ-PSK " +#define DPP_EVENT_CONNECTOR "DPP-CONNECTOR " +#define DPP_EVENT_C_SIGN_KEY "DPP-C-SIGN-KEY " +#define DPP_EVENT_NET_ACCESS_KEY "DPP-NET-ACCESS-KEY " +#define DPP_EVENT_MISSING_CONNECTOR "DPP-MISSING-CONNECTOR " +#define DPP_EVENT_NETWORK_ID "DPP-NETWORK-ID " +#define DPP_EVENT_RX "DPP-RX " +#define DPP_EVENT_TX "DPP-TX " +#define DPP_EVENT_TX_STATUS "DPP-TX-STATUS " +#define DPP_EVENT_FAIL "DPP-FAIL " +#define DPP_EVENT_PKEX_T_LIMIT "DPP-PKEX-T-LIMIT " +#define DPP_EVENT_INTRO "DPP-INTRO " +#define DPP_EVENT_CONF_REQ_RX "DPP-CONF-REQ-RX " + /* MESH events */ #define MESH_GROUP_STARTED "MESH-GROUP-STARTED " #define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED " @@ -232,9 +273,14 @@ extern "C" { #define RX_HS20_ANQP "RX-HS20-ANQP " #define RX_HS20_ANQP_ICON "RX-HS20-ANQP-ICON " #define RX_HS20_ICON "RX-HS20-ICON " +#define RX_MBO_ANQP "RX-MBO-ANQP " + +/* parameters: <Venue Number> <Venue URL> */ +#define RX_VENUE_URL "RX-VENUE-URL " #define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION " #define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE " +#define HS20_T_C_ACCEPTANCE "HS20-T-C-ACCEPTANCE " #define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START " #define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT " @@ -258,6 +304,9 @@ extern "C" { #define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA " #define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA " +#define HS20_T_C_FILTERING_ADD "HS20-T-C-FILTERING-ADD " +#define HS20_T_C_FILTERING_REMOVE "HS20-T-C-FILTERING-REMOVE " + #define AP_EVENT_ENABLED "AP-ENABLED " #define AP_EVENT_DISABLED "AP-DISABLED " @@ -273,6 +322,7 @@ extern "C" { #define DFS_EVENT_CAC_START "DFS-CAC-START " #define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED " #define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED " +#define DFS_EVENT_PRE_CAC_EXPIRED "DFS-PRE-CAC-EXPIRED " #define AP_CSA_FINISHED "AP-CSA-FINISHED " @@ -282,12 +332,46 @@ extern "C" { /* BSS Transition Management Response frame received */ #define BSS_TM_RESP "BSS-TM-RESP " +/* Collocated Interference Request frame received; + * parameters: <dialog token> <automatic report enabled> <report timeout> */ +#define COLOC_INTF_REQ "COLOC-INTF-REQ " +/* Collocated Interference Report frame received; + * parameters: <STA address> <dialog token> <hexdump of report elements> */ +#define COLOC_INTF_REPORT "COLOC-INTF-REPORT " + /* MBO IE with cellular data connection preference received */ #define MBO_CELL_PREFERENCE "MBO-CELL-PREFERENCE " /* BSS Transition Management Request received with MBO transition reason */ #define MBO_TRANSITION_REASON "MBO-TRANSITION-REASON " +/* parameters: <STA address> <dialog token> <ack=0/1> */ +#define BEACON_REQ_TX_STATUS "BEACON-REQ-TX-STATUS " +/* parameters: <STA address> <dialog token> <report mode> <beacon report> */ +#define BEACON_RESP_RX "BEACON-RESP-RX " + +/* PMKSA cache entry added; parameters: <BSSID> <network_id> */ +#define PMKSA_CACHE_ADDED "PMKSA-CACHE-ADDED " +/* PMKSA cache entry removed; parameters: <BSSID> <network_id> */ +#define PMKSA_CACHE_REMOVED "PMKSA-CACHE-REMOVED " + +/* FILS HLP Container receive; parameters: dst=<addr> src=<addr> frame=<hexdump> + */ +#define FILS_HLP_RX "FILS-HLP-RX " + +/* Event to indicate Probe Request frame; + * parameters: sa=<STA MAC address> signal=<signal> */ +#define RX_PROBE_REQUEST "RX-PROBE-REQUEST " + +/* Event to indicate station's HT/VHT operation mode change information */ +#define STA_OPMODE_MAX_BW_CHANGED "STA-OPMODE-MAX-BW-CHANGED " +#define STA_OPMODE_SMPS_MODE_CHANGED "STA-OPMODE-SMPS-MODE-CHANGED " +#define STA_OPMODE_N_SS_CHANGED "STA-OPMODE-N_SS-CHANGED " + +/* New interface addition or removal for 4addr WDS SDA */ +#define WDS_STA_INTERFACE_ADDED "WDS-STA-INTERFACE-ADDED " +#define WDS_STA_INTERFACE_REMOVED "WDS-STA-INTERFACE-REMOVED " + /* BSS command information masks */ #define WPA_BSS_MASK_ALL 0xFFFDFFFF @@ -313,6 +397,9 @@ extern "C" { #define WPA_BSS_MASK_SNR BIT(19) #define WPA_BSS_MASK_EST_THROUGHPUT BIT(20) #define WPA_BSS_MASK_FST BIT(21) +#define WPA_BSS_MASK_UPDATE_IDX BIT(22) +#define WPA_BSS_MASK_BEACON_IE BIT(23) +#define WPA_BSS_MASK_FILS_INDICATION BIT(24) /* VENDOR_ELEM_* frame id values */ @@ -386,7 +473,7 @@ void wpa_ctrl_close(struct wpa_ctrl *ctrl); * * This function is used to send commands to wpa_supplicant/hostapd. Received * response will be written to reply and reply_len is set to the actual length - * of the reply. This function will block for up to two seconds while waiting + * of the reply. This function will block for up to 10 seconds while waiting * for the reply. If unsolicited messages are received, the blocking time may * be longer. * diff --git a/src/common/wpa_helpers.c b/src/common/wpa_helpers.c index f1594213f97f..8e1c09ec5929 100644 --- a/src/common/wpa_helpers.c +++ b/src/common/wpa_helpers.c @@ -222,7 +222,8 @@ int wait_ip_addr(const char *ifname, int timeout) if (get_wpa_status(ifname, "ip_address", ip, sizeof(ip)) == 0 && strlen(ip) > 0) { printf("IP address found: '%s'\n", ip); - return 0; + if (strncmp(ip, "169.254.", 8) != 0) + return 0; } ctrl = wpa_open_ctrl(ifname); if (ctrl == NULL) diff --git a/src/crypto/Makefile b/src/crypto/Makefile index d181e723134e..ee93e41fbbb1 100644 --- a/src/crypto/Makefile +++ b/src/crypto/Makefile @@ -14,6 +14,9 @@ CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER #CFLAGS += -DALL_DH_GROUPS CFLAGS += -DCONFIG_SHA256 +CFLAGS += -DCONFIG_SHA384 +CFLAGS += -DCONFIG_HMAC_SHA384_KDF +CFLAGS += -DCONFIG_INTERNAL_SHA384 LIB_OBJS= \ aes-cbc.o \ @@ -48,6 +51,8 @@ LIB_OBJS= \ sha256-prf.o \ sha256-tlsprf.o \ sha256-internal.o \ + sha384.o \ + sha384-prf.o \ sha384-internal.o \ sha512-internal.o diff --git a/src/crypto/aes-ctr.c b/src/crypto/aes-ctr.c index d4d874daacd0..e27f3bbd0706 100644 --- a/src/crypto/aes-ctr.c +++ b/src/crypto/aes-ctr.c @@ -1,5 +1,5 @@ /* - * AES-128 CTR + * AES-128/192/256 CTR * * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> * @@ -14,15 +14,16 @@ #include "aes_wrap.h" /** - * aes_128_ctr_encrypt - AES-128 CTR mode encryption - * @key: Key for encryption (16 bytes) + * aes_ctr_encrypt - AES-128/192/256 CTR mode encryption + * @key: Key for encryption (key_len bytes) + * @key_len: Length of the key (16, 24, or 32 bytes) * @nonce: Nonce for counter mode (16 bytes) * @data: Data to encrypt in-place * @data_len: Length of data in bytes * Returns: 0 on success, -1 on failure */ -int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, - u8 *data, size_t data_len) +int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce, + u8 *data, size_t data_len) { void *ctx; size_t j, len, left = data_len; @@ -30,7 +31,7 @@ int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, u8 *pos = data; u8 counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE]; - ctx = aes_encrypt_init(key, 16); + ctx = aes_encrypt_init(key, key_len); if (ctx == NULL) return -1; os_memcpy(counter, nonce, AES_BLOCK_SIZE); @@ -53,3 +54,18 @@ int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, aes_encrypt_deinit(ctx); return 0; } + + +/** + * aes_128_ctr_encrypt - AES-128 CTR mode encryption + * @key: Key for encryption (key_len bytes) + * @nonce: Nonce for counter mode (16 bytes) + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes + * Returns: 0 on success, -1 on failure + */ +int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, + u8 *data, size_t data_len) +{ + return aes_ctr_encrypt(key, 16, nonce, data, data_len); +} diff --git a/src/crypto/aes-internal-dec.c b/src/crypto/aes-internal-dec.c index 720c7036e4e7..748229594904 100644 --- a/src/crypto/aes-internal-dec.c +++ b/src/crypto/aes-internal-dec.c @@ -147,10 +147,12 @@ d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3] PUTU32(pt + 12, s3); } -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) + +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { u32 *rk = ctx; rijndaelDecrypt(ctx, rk[AES_PRIV_NR_POS], crypt, plain); + return 0; } diff --git a/src/crypto/aes-internal-enc.c b/src/crypto/aes-internal-enc.c index f3c61b8508f3..9fdb4f35cb9b 100644 --- a/src/crypto/aes-internal-enc.c +++ b/src/crypto/aes-internal-enc.c @@ -112,10 +112,11 @@ void * aes_encrypt_init(const u8 *key, size_t len) } -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { u32 *rk = ctx; rijndaelEncrypt(ctx, rk[AES_PRIV_NR_POS], plain, crypt); + return 0; } diff --git a/src/crypto/aes-siv.c b/src/crypto/aes-siv.c index 5ac82c2e4b5c..b682f3ad565d 100644 --- a/src/crypto/aes-siv.c +++ b/src/crypto/aes-siv.c @@ -61,26 +61,33 @@ static void pad_block(u8 *pad, const u8 *addr, size_t len) } -static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[], - size_t *len, u8 *mac) +static int aes_s2v(const u8 *key, size_t key_len, + size_t num_elem, const u8 *addr[], size_t *len, u8 *mac) { u8 tmp[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE]; u8 *buf = NULL; int ret; size_t i; + const u8 *data[1]; + size_t data_len[1]; if (!num_elem) { os_memcpy(tmp, zero, sizeof(zero)); tmp[AES_BLOCK_SIZE - 1] = 1; - return omac1_aes_128(key, tmp, sizeof(tmp), mac); + data[0] = tmp; + data_len[0] = sizeof(tmp); + return omac1_aes_vector(key, key_len, 1, data, data_len, mac); } - ret = omac1_aes_128(key, zero, sizeof(zero), tmp); + data[0] = zero; + data_len[0] = sizeof(zero); + ret = omac1_aes_vector(key, key_len, 1, data, data_len, tmp); if (ret) return ret; for (i = 0; i < num_elem - 1; i++) { - ret = omac1_aes_128(key, addr[i], len[i], tmp2); + ret = omac1_aes_vector(key, key_len, 1, &addr[i], &len[i], + tmp2); if (ret) return ret; @@ -88,13 +95,13 @@ static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[], xor(tmp, tmp2); } if (len[i] >= AES_BLOCK_SIZE) { - buf = os_malloc(len[i]); + buf = os_memdup(addr[i], len[i]); if (!buf) return -ENOMEM; - os_memcpy(buf, addr[i], len[i]); xorend(buf, len[i], tmp, AES_BLOCK_SIZE); - ret = omac1_aes_128(key, buf, len[i], mac); + data[0] = buf; + ret = omac1_aes_vector(key, key_len, 1, data, &len[i], mac); bin_clear_free(buf, len[i]); return ret; } @@ -103,24 +110,32 @@ static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[], pad_block(tmp2, addr[i], len[i]); xor(tmp, tmp2); - return omac1_aes_128(key, tmp, sizeof(tmp), mac); + data[0] = tmp; + data_len[0] = sizeof(tmp); + return omac1_aes_vector(key, key_len, 1, data, data_len, mac); } -int aes_siv_encrypt(const u8 *key, const u8 *pw, - size_t pwlen, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *out) +int aes_siv_encrypt(const u8 *key, size_t key_len, + const u8 *pw, size_t pwlen, + size_t num_elem, const u8 *addr[], const size_t *len, + u8 *out) { const u8 *_addr[6]; size_t _len[6]; - const u8 *k1 = key, *k2 = key + 16; + const u8 *k1, *k2; u8 v[AES_BLOCK_SIZE]; size_t i; u8 *iv, *crypt_pw; - if (num_elem > ARRAY_SIZE(_addr) - 1) + if (num_elem > ARRAY_SIZE(_addr) - 1 || + (key_len != 32 && key_len != 48 && key_len != 64)) return -1; + key_len /= 2; + k1 = key; + k2 = key + key_len; + for (i = 0; i < num_elem; i++) { _addr[i] = addr[i]; _len[i] = len[i]; @@ -128,7 +143,7 @@ int aes_siv_encrypt(const u8 *key, const u8 *pw, _addr[num_elem] = pw; _len[num_elem] = pwlen; - if (aes_s2v(k1, num_elem + 1, _addr, _len, v)) + if (aes_s2v(k1, key_len, num_elem + 1, _addr, _len, v)) return -1; iv = out; @@ -140,26 +155,31 @@ int aes_siv_encrypt(const u8 *key, const u8 *pw, /* zero out 63rd and 31st bits of ctr (from right) */ v[8] &= 0x7f; v[12] &= 0x7f; - return aes_128_ctr_encrypt(k2, v, crypt_pw, pwlen); + return aes_ctr_encrypt(k2, key_len, v, crypt_pw, pwlen); } -int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len, +int aes_siv_decrypt(const u8 *key, size_t key_len, + const u8 *iv_crypt, size_t iv_c_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *out) { const u8 *_addr[6]; size_t _len[6]; - const u8 *k1 = key, *k2 = key + 16; + const u8 *k1, *k2; size_t crypt_len; size_t i; int ret; u8 iv[AES_BLOCK_SIZE]; u8 check[AES_BLOCK_SIZE]; - if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1) + if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1 || + (key_len != 32 && key_len != 48 && key_len != 64)) return -1; crypt_len = iv_c_len - AES_BLOCK_SIZE; + key_len /= 2; + k1 = key; + k2 = key + key_len; for (i = 0; i < num_elem; i++) { _addr[i] = addr[i]; @@ -174,11 +194,11 @@ int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len, iv[8] &= 0x7f; iv[12] &= 0x7f; - ret = aes_128_ctr_encrypt(k2, iv, out, crypt_len); + ret = aes_ctr_encrypt(k2, key_len, iv, out, crypt_len); if (ret) return ret; - ret = aes_s2v(k1, num_elem + 1, _addr, _len, check); + ret = aes_s2v(k1, key_len, num_elem + 1, _addr, _len, check); if (ret) return ret; if (os_memcmp(check, iv_crypt, AES_BLOCK_SIZE) == 0) diff --git a/src/crypto/aes.h b/src/crypto/aes.h index 2de59e04efa5..8ab3de2ee83f 100644 --- a/src/crypto/aes.h +++ b/src/crypto/aes.h @@ -12,10 +12,10 @@ #define AES_BLOCK_SIZE 16 void * aes_encrypt_init(const u8 *key, size_t len); -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); void aes_encrypt_deinit(void *ctx); void * aes_decrypt_init(const u8 *key, size_t len); -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); void aes_decrypt_deinit(void *ctx); #endif /* AES_H */ diff --git a/src/crypto/aes_siv.h b/src/crypto/aes_siv.h index 463cf6536107..fb05d80c1f12 100644 --- a/src/crypto/aes_siv.h +++ b/src/crypto/aes_siv.h @@ -9,10 +9,12 @@ #ifndef AES_SIV_H #define AES_SIV_H -int aes_siv_encrypt(const u8 *key, const u8 *pw, - size_t pwlen, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *out); -int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len, +int aes_siv_encrypt(const u8 *key, size_t key_len, + const u8 *pw, size_t pwlen, + size_t num_elem, const u8 *addr[], const size_t *len, + u8 *out); +int aes_siv_decrypt(const u8 *key, size_t key_len, + const u8 *iv_crypt, size_t iv_c_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *out); diff --git a/src/crypto/aes_wrap.h b/src/crypto/aes_wrap.h index 4a142093b0d6..b70b1d26e550 100644 --- a/src/crypto/aes_wrap.h +++ b/src/crypto/aes_wrap.h @@ -3,7 +3,7 @@ * * - AES Key Wrap Algorithm (RFC3394) * - One-Key CBC MAC (OMAC1) hash with AES-128 and AES-256 - * - AES-128 CTR mode encryption + * - AES-128/192/256 CTR mode encryption * - AES-128 EAX mode encryption/decryption * - AES-128 CBC * - AES-GCM @@ -33,6 +33,8 @@ int __must_check omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, int __must_check omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac); int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out); +int __must_check aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce, + u8 *data, size_t data_len); int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, u8 *data, size_t data_len); int __must_check aes_128_eax_encrypt(const u8 *key, diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index bdc3ba6f37e0..507b7cab86fc 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -1,6 +1,6 @@ /* * Wrapper functions for crypto libraries - * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -106,8 +106,9 @@ int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, * @clear: 8 octets (in) * @key: 7 octets (in) (no parity bits included) * @cypher: 8 octets (out) + * Returns: 0 on success, -1 on failure */ -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher); +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher); /** * aes_encrypt_init - Initialize AES for encryption @@ -122,8 +123,9 @@ void * aes_encrypt_init(const u8 *key, size_t len); * @ctx: Context pointer from aes_encrypt_init() * @plain: Plaintext data to be encrypted (16 bytes) * @crypt: Buffer for the encrypted data (16 bytes) + * Returns: 0 on success, -1 on failure */ -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); /** * aes_encrypt_deinit - Deinitialize AES encryption @@ -144,8 +146,9 @@ void * aes_decrypt_init(const u8 *key, size_t len); * @ctx: Context pointer from aes_encrypt_init() * @crypt: Encrypted data (16 bytes) * @plain: Buffer for the decrypted data (16 bytes) + * Returns: 0 on success, -1 on failure */ -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); /** * aes_decrypt_deinit - Deinitialize AES decryption @@ -414,6 +417,13 @@ int __must_check crypto_public_key_decrypt_pkcs1( struct crypto_public_key *key, const u8 *crypt, size_t crypt_len, u8 *plain, size_t *plain_len); +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey); +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len); + /** * crypto_global_init - Initialize crypto wrapper * @@ -526,6 +536,14 @@ int crypto_bignum_to_bin(const struct crypto_bignum *a, u8 *buf, size_t buflen, size_t padlen); /** + * crypto_bignum_rand - Create a random number in range of modulus + * @r: Bignum; set to a random value + * @m: Bignum; modulus + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m); + +/** * crypto_bignum_add - c = a + b * @a: Bignum * @b: Bignum @@ -607,6 +625,16 @@ int crypto_bignum_mulmod(const struct crypto_bignum *a, struct crypto_bignum *d); /** + * crypto_bignum_rshift - r = a >> n + * @a: Bignum + * @n: Number of bits + * @r: Bignum; used to store the result of a >> n + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_rshift(const struct crypto_bignum *a, int n, + struct crypto_bignum *r); + +/** * crypto_bignum_cmp - Compare two bignums * @a: Bignum * @b: Bignum @@ -637,6 +665,13 @@ int crypto_bignum_is_zero(const struct crypto_bignum *a); int crypto_bignum_is_one(const struct crypto_bignum *a); /** + * crypto_bignum_is_odd - Is the given bignum odd + * @a: Bignum + * Returns: 1 if @a is odd or 0 if not + */ +int crypto_bignum_is_odd(const struct crypto_bignum *a); + +/** * crypto_bignum_legendre - Compute the Legendre symbol (a/p) * @a: Bignum * @p: Bignum @@ -668,6 +703,14 @@ struct crypto_ec * crypto_ec_init(int group); void crypto_ec_deinit(struct crypto_ec *e); /** + * crypto_ec_cofactor - Set the cofactor into the big number + * @e: EC context from crypto_ec_init() + * @cofactor: Cofactor of curve. + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor); + +/** * crypto_ec_prime_len - Get length of the prime in octets * @e: EC context from crypto_ec_init() * Returns: Length of the prime defining the group @@ -682,6 +725,13 @@ size_t crypto_ec_prime_len(struct crypto_ec *e); size_t crypto_ec_prime_len_bits(struct crypto_ec *e); /** + * crypto_ec_order_len - Get length of the order in octets + * @e: EC context from crypto_ec_init() + * Returns: Length of the order defining the group + */ +size_t crypto_ec_order_len(struct crypto_ec *e); + +/** * crypto_ec_get_prime - Get prime defining an EC group * @e: EC context from crypto_ec_init() * Returns: Prime (bignum) defining the group @@ -718,6 +768,16 @@ struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e); void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear); /** + * crypto_ec_point_x - Copies the x-ordinate point into big number + * @e: EC context from crypto_ec_init() + * @p: EC point data + * @x: Big number to set to the copy of x-ordinate + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p, + struct crypto_bignum *x); + +/** * crypto_ec_point_to_bin - Write EC point value as binary data * @e: EC context from crypto_ec_init() * @p: EC point data from crypto_ec_point_init() @@ -746,7 +806,7 @@ struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, const u8 *val); /** - * crypto_bignum_add - c = a + b + * crypto_ec_point_add - c = a + b * @e: EC context from crypto_ec_init() * @a: Bignum * @b: Bignum @@ -758,7 +818,7 @@ int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, struct crypto_ec_point *c); /** - * crypto_bignum_mul - res = b * p + * crypto_ec_point_mul - res = b * p * @e: EC context from crypto_ec_init() * @p: EC point * @b: Bignum @@ -829,4 +889,12 @@ int crypto_ec_point_cmp(const struct crypto_ec *e, const struct crypto_ec_point *a, const struct crypto_ec_point *b); +struct crypto_ecdh; + +struct crypto_ecdh * crypto_ecdh_init(int group); +struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y); +struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y, + const u8 *key, size_t len); +void crypto_ecdh_deinit(struct crypto_ecdh *ecdh); + #endif /* CRYPTO_H */ diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c index 0dfd54d22d47..7a797b5c359d 100644 --- a/src/crypto/crypto_gnutls.c +++ b/src/crypto/crypto_gnutls.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / wrapper functions for libgcrypt - * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,27 +10,42 @@ #include <gcrypt.h> #include "common.h" +#include "md5.h" +#include "sha1.h" +#include "sha256.h" +#include "sha384.h" +#include "sha512.h" #include "crypto.h" -int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +static int gnutls_digest_vector(int algo, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) { gcry_md_hd_t hd; unsigned char *p; size_t i; - if (gcry_md_open(&hd, GCRY_MD_MD4, 0) != GPG_ERR_NO_ERROR) + if (TEST_FAIL()) + return -1; + + if (gcry_md_open(&hd, algo, 0) != GPG_ERR_NO_ERROR) return -1; for (i = 0; i < num_elem; i++) gcry_md_write(hd, addr[i], len[i]); - p = gcry_md_read(hd, GCRY_MD_MD4); + p = gcry_md_read(hd, algo); if (p) - memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD4)); + memcpy(mac, p, gcry_md_get_algo_dlen(algo)); gcry_md_close(hd); return 0; } -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_digest_vector(GCRY_MD_MD4, num_elem, addr, len, mac); +} + + +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { gcry_cipher_hd_t hd; u8 pkey[8], next, tmp; @@ -49,49 +64,161 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) gcry_err_code(gcry_cipher_setkey(hd, pkey, 8)); gcry_cipher_encrypt(hd, cypher, 8, clear, 8); gcry_cipher_close(hd); + return 0; } int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - gcry_md_hd_t hd; - unsigned char *p; - size_t i; - - if (gcry_md_open(&hd, GCRY_MD_MD5, 0) != GPG_ERR_NO_ERROR) - return -1; - for (i = 0; i < num_elem; i++) - gcry_md_write(hd, addr[i], len[i]); - p = gcry_md_read(hd, GCRY_MD_MD5); - if (p) - memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD5)); - gcry_md_close(hd); - return 0; + return gnutls_digest_vector(GCRY_MD_MD5, num_elem, addr, len, mac); } int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { + return gnutls_digest_vector(GCRY_MD_SHA1, num_elem, addr, len, mac); +} + + +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_digest_vector(GCRY_MD_SHA256, num_elem, addr, len, mac); +} + + +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_digest_vector(GCRY_MD_SHA384, num_elem, addr, len, mac); +} + + +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_digest_vector(GCRY_MD_SHA512, num_elem, addr, len, mac); +} + + +static int gnutls_hmac_vector(int algo, const u8 *key, size_t key_len, + size_t num_elem, const u8 *addr[], + const size_t *len, u8 *mac) +{ gcry_md_hd_t hd; unsigned char *p; size_t i; - if (gcry_md_open(&hd, GCRY_MD_SHA1, 0) != GPG_ERR_NO_ERROR) + if (TEST_FAIL()) + return -1; + + if (gcry_md_open(&hd, algo, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR) + return -1; + if (gcry_md_setkey(hd, key, key_len) != GPG_ERR_NO_ERROR) { + gcry_md_close(hd); return -1; + } for (i = 0; i < num_elem; i++) gcry_md_write(hd, addr[i], len[i]); - p = gcry_md_read(hd, GCRY_MD_SHA1); + p = gcry_md_read(hd, algo); if (p) - memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_SHA1)); + memcpy(mac, p, gcry_md_get_algo_dlen(algo)); gcry_md_close(hd); return 0; } +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_hmac_vector(GCRY_MD_MD5, key, key_len, num_elem, addr, + len, mac); +} + + +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} + + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_hmac_vector(GCRY_MD_SHA1, key, key_len, num_elem, addr, + len, mac); +} + + +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + + +#ifdef CONFIG_SHA256 + +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_hmac_vector(GCRY_MD_SHA256, key, key_len, num_elem, addr, + len, mac); +} + + +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA256 */ + + +#ifdef CONFIG_SHA384 + +int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_hmac_vector(GCRY_MD_SHA384, key, key_len, num_elem, addr, + len, mac); +} + + +int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA384 */ + + +#ifdef CONFIG_SHA512 + +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_hmac_vector(GCRY_MD_SHA512, key, key_len, num_elem, addr, + len, mac); +} + + +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA512 */ + + void * aes_encrypt_init(const u8 *key, size_t len) { gcry_cipher_hd_t hd; + if (TEST_FAIL()) + return NULL; + if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) != GPG_ERR_NO_ERROR) { printf("cipher open failed\n"); @@ -107,10 +234,11 @@ void * aes_encrypt_init(const u8 *key, size_t len) } -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { gcry_cipher_hd_t hd = ctx; gcry_cipher_encrypt(hd, crypt, 16, plain, 16); + return 0; } @@ -125,6 +253,9 @@ void * aes_decrypt_init(const u8 *key, size_t len) { gcry_cipher_hd_t hd; + if (TEST_FAIL()) + return NULL; + if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) != GPG_ERR_NO_ERROR) return NULL; @@ -137,10 +268,11 @@ void * aes_decrypt_init(const u8 *key, size_t len) } -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { gcry_cipher_hd_t hd = ctx; gcry_cipher_decrypt(hd, plain, 16, crypt, 16); + return 0; } @@ -151,6 +283,42 @@ void aes_decrypt_deinit(void *ctx) } +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) +{ + size_t pubkey_len, pad; + + if (os_get_random(privkey, prime_len) < 0) + return -1; + if (os_memcmp(privkey, prime, prime_len) > 0) { + /* Make sure private value is smaller than prime */ + privkey[0] = 0; + } + + pubkey_len = prime_len; + if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len, + pubkey, &pubkey_len) < 0) + return -1; + if (pubkey_len < prime_len) { + pad = prime_len - pubkey_len; + os_memmove(pubkey + pad, pubkey, pubkey_len); + os_memset(pubkey, 0, pad); + } + + return 0; +} + + +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) +{ + return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +} + + int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, diff --git a/src/crypto/crypto_internal-modexp.c b/src/crypto/crypto_internal-modexp.c index 9dcabb95bdd2..92581ac676d3 100644 --- a/src/crypto/crypto_internal-modexp.c +++ b/src/crypto/crypto_internal-modexp.c @@ -13,6 +13,42 @@ #include "crypto.h" +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) +{ + size_t pubkey_len, pad; + + if (os_get_random(privkey, prime_len) < 0) + return -1; + if (os_memcmp(privkey, prime, prime_len) > 0) { + /* Make sure private value is smaller than prime */ + privkey[0] = 0; + } + + pubkey_len = prime_len; + if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len, + pubkey, &pubkey_len) < 0) + return -1; + if (pubkey_len < prime_len) { + pad = prime_len - pubkey_len; + os_memmove(pubkey + pad, pubkey, pubkey_len); + os_memset(pubkey, 0, pad); + } + + return 0; +} + + +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) +{ + return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +} + + int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c index a55edd14e2d3..259f99500bcd 100644 --- a/src/crypto/crypto_libtomcrypt.c +++ b/src/crypto/crypto_libtomcrypt.c @@ -35,7 +35,7 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) } -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { u8 pkey[8], next, tmp; int i; @@ -53,6 +53,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) des_setup(pkey, 8, 0, &skey); des_ecb_encrypt(clear, cypher, &skey); des_done(&skey); + return 0; } @@ -96,10 +97,10 @@ void * aes_encrypt_init(const u8 *key, size_t len) } -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { symmetric_key *skey = ctx; - aes_ecb_encrypt(plain, crypt, skey); + return aes_ecb_encrypt(plain, crypt, skey) == CRYPT_OK ? 0 : -1; } @@ -125,10 +126,10 @@ void * aes_decrypt_init(const u8 *key, size_t len) } -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { symmetric_key *skey = ctx; - aes_ecb_encrypt(plain, (u8 *) crypt, skey); + return aes_ecb_encrypt(plain, (u8 *) crypt, skey) == CRYPT_OK ? 0 : -1; } @@ -297,7 +298,7 @@ struct crypto_cipher { struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, const u8 *iv, const u8 *key, size_t key_len) -{ +{ struct crypto_cipher *ctx; int idx, res, rc4 = 0; @@ -693,6 +694,42 @@ void crypto_global_deinit(void) #ifdef CONFIG_MODEXP +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) +{ + size_t pubkey_len, pad; + + if (os_get_random(privkey, prime_len) < 0) + return -1; + if (os_memcmp(privkey, prime, prime_len) > 0) { + /* Make sure private value is smaller than prime */ + privkey[0] = 0; + } + + pubkey_len = prime_len; + if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len, + pubkey, &pubkey_len) < 0) + return -1; + if (pubkey_len < prime_len) { + pad = prime_len - pubkey_len; + os_memmove(pubkey + pad, pubkey, pubkey_len); + os_memset(pubkey, 0, pad); + } + + return 0; +} + + +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) +{ + return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +} + + int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, diff --git a/src/crypto/crypto_linux.c b/src/crypto/crypto_linux.c new file mode 100644 index 000000000000..8099193bf068 --- /dev/null +++ b/src/crypto/crypto_linux.c @@ -0,0 +1,1006 @@ +/* + * Crypto wrapper for Linux kernel AF_ALG + * Copyright (c) 2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include <linux/if_alg.h> + +#include "common.h" +#include "crypto.h" +#include "md5.h" +#include "sha1.h" +#include "sha256.h" +#include "sha384.h" +#include "aes.h" + + +#ifndef SOL_ALG +#define SOL_ALG 279 +#endif /* SOL_ALG */ + + +static int linux_af_alg_socket(const char *type, const char *name) +{ + struct sockaddr_alg sa; + int s; + + if (TEST_FAIL()) + return -1; + + s = socket(AF_ALG, SOCK_SEQPACKET, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, "%s: Failed to open AF_ALG socket: %s", + __func__, strerror(errno)); + return -1; + } + + os_memset(&sa, 0, sizeof(sa)); + sa.salg_family = AF_ALG; + os_strlcpy((char *) sa.salg_type, type, sizeof(sa.salg_type)); + os_strlcpy((char *) sa.salg_name, name, sizeof(sa.salg_type)); + if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) { + wpa_printf(MSG_ERROR, + "%s: Failed to bind AF_ALG socket(%s,%s): %s", + __func__, type, name, strerror(errno)); + close(s); + return -1; + } + + return s; +} + + +static int linux_af_alg_hash_vector(const char *alg, const u8 *key, + size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, + u8 *mac, size_t mac_len) +{ + int s, t; + size_t i; + ssize_t res; + int ret = -1; + + s = linux_af_alg_socket("hash", alg); + if (s < 0) + return -1; + + if (key && setsockopt(s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) { + wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s", + __func__, strerror(errno)); + close(s); + return -1; + } + + t = accept(s, NULL, NULL); + if (t < 0) { + wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s", + __func__, strerror(errno)); + close(s); + return -1; + } + + for (i = 0; i < num_elem; i++) { + res = send(t, addr[i], len[i], i + 1 < num_elem ? MSG_MORE : 0); + if (res < 0) { + wpa_printf(MSG_ERROR, + "%s: send on AF_ALG socket failed: %s", + __func__, strerror(errno)); + goto fail; + } + if ((size_t) res < len[i]) { + wpa_printf(MSG_ERROR, + "%s: send on AF_ALG socket did not accept full buffer (%d/%d)", + __func__, (int) res, (int) len[i]); + goto fail; + } + } + + res = recv(t, mac, mac_len, 0); + if (res < 0) { + wpa_printf(MSG_ERROR, + "%s: recv on AF_ALG socket failed: %s", + __func__, strerror(errno)); + goto fail; + } + if ((size_t) res < mac_len) { + wpa_printf(MSG_ERROR, + "%s: recv on AF_ALG socket did not return full buffer (%d/%d)", + __func__, (int) res, (int) mac_len); + goto fail; + } + + ret = 0; +fail: + close(t); + close(s); + + return ret; +} + + +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("md4", NULL, 0, num_elem, addr, len, + mac, 16); +} + + +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("md5", NULL, 0, num_elem, addr, len, + mac, MD5_MAC_LEN); +} + + +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return linux_af_alg_hash_vector("sha1", NULL, 0, num_elem, addr, len, + mac, SHA1_MAC_LEN); +} + + +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return linux_af_alg_hash_vector("sha256", NULL, 0, num_elem, addr, len, + mac, SHA256_MAC_LEN); +} + + +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return linux_af_alg_hash_vector("sha384", NULL, 0, num_elem, addr, len, + mac, SHA384_MAC_LEN); +} + + +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return linux_af_alg_hash_vector("sha512", NULL, 0, num_elem, addr, len, + mac, 64); +} + + +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("hmac(md5)", key, key_len, num_elem, + addr, len, mac, 16); +} + + +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} + + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("hmac(sha1)", key, key_len, num_elem, + addr, len, mac, SHA1_MAC_LEN); +} + + +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + + +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("hmac(sha256)", key, key_len, num_elem, + addr, len, mac, SHA256_MAC_LEN); +} + + +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + + +int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("hmac(sha384)", key, key_len, num_elem, + addr, len, mac, SHA384_MAC_LEN); +} + + +int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac); +} + + +struct crypto_hash { + int s; + int t; + size_t mac_len; + int failed; +}; + + +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct crypto_hash *ctx; + const char *name; + + ctx = os_zalloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + switch (alg) { + case CRYPTO_HASH_ALG_MD5: + name = "md5"; + ctx->mac_len = MD5_MAC_LEN; + break; + case CRYPTO_HASH_ALG_SHA1: + name = "sha1"; + ctx->mac_len = SHA1_MAC_LEN; + break; + case CRYPTO_HASH_ALG_HMAC_MD5: + name = "hmac(md5)"; + ctx->mac_len = MD5_MAC_LEN; + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + name = "hmac(sha1)"; + ctx->mac_len = SHA1_MAC_LEN; + break; + case CRYPTO_HASH_ALG_SHA256: + name = "sha256"; + ctx->mac_len = SHA256_MAC_LEN; + break; + case CRYPTO_HASH_ALG_HMAC_SHA256: + name = "hmac(sha256)"; + ctx->mac_len = SHA256_MAC_LEN; + break; + case CRYPTO_HASH_ALG_SHA384: + name = "sha384"; + ctx->mac_len = SHA384_MAC_LEN; + break; + case CRYPTO_HASH_ALG_SHA512: + name = "sha512"; + ctx->mac_len = 64; + break; + default: + os_free(ctx); + return NULL; + } + + ctx->s = linux_af_alg_socket("hash", name); + if (ctx->s < 0) { + os_free(ctx); + return NULL; + } + + if (key && key_len && + setsockopt(ctx->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) { + wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s", + __func__, strerror(errno)); + close(ctx->s); + os_free(ctx); + return NULL; + } + + ctx->t = accept(ctx->s, NULL, NULL); + if (ctx->t < 0) { + wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s", + __func__, strerror(errno)); + close(ctx->s); + os_free(ctx); + return NULL; + } + + return ctx; +} + + +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + ssize_t res; + + if (!ctx) + return; + + res = send(ctx->t, data, len, MSG_MORE); + if (res < 0) { + wpa_printf(MSG_ERROR, + "%s: send on AF_ALG socket failed: %s", + __func__, strerror(errno)); + ctx->failed = 1; + return; + } + if ((size_t) res < len) { + wpa_printf(MSG_ERROR, + "%s: send on AF_ALG socket did not accept full buffer (%d/%d)", + __func__, (int) res, (int) len); + ctx->failed = 1; + return; + } +} + + +static void crypto_hash_deinit(struct crypto_hash *ctx) +{ + close(ctx->s); + close(ctx->t); + os_free(ctx); +} + + +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + ssize_t res; + + if (!ctx) + return -2; + + if (!mac || !len) { + crypto_hash_deinit(ctx); + return 0; + } + + if (ctx->failed) { + crypto_hash_deinit(ctx); + return -2; + } + + if (*len < ctx->mac_len) { + crypto_hash_deinit(ctx); + *len = ctx->mac_len; + return -1; + } + *len = ctx->mac_len; + + res = recv(ctx->t, mac, ctx->mac_len, 0); + if (res < 0) { + wpa_printf(MSG_ERROR, + "%s: recv on AF_ALG socket failed: %s", + __func__, strerror(errno)); + crypto_hash_deinit(ctx); + return -2; + } + if ((size_t) res < ctx->mac_len) { + wpa_printf(MSG_ERROR, + "%s: recv on AF_ALG socket did not return full buffer (%d/%d)", + __func__, (int) res, (int) ctx->mac_len); + crypto_hash_deinit(ctx); + return -2; + } + + crypto_hash_deinit(ctx); + return 0; +} + + +struct linux_af_alg_skcipher { + int s; + int t; +}; + + +static void linux_af_alg_skcipher_deinit(struct linux_af_alg_skcipher *skcipher) +{ + if (!skcipher) + return; + if (skcipher->s >= 0) + close(skcipher->s); + if (skcipher->t >= 0) + close(skcipher->t); + os_free(skcipher); +} + + +static struct linux_af_alg_skcipher * +linux_af_alg_skcipher(const char *alg, const u8 *key, size_t key_len) +{ + struct linux_af_alg_skcipher *skcipher; + + skcipher = os_zalloc(sizeof(*skcipher)); + if (!skcipher) + goto fail; + skcipher->t = -1; + + skcipher->s = linux_af_alg_socket("skcipher", alg); + if (skcipher->s < 0) + goto fail; + + if (setsockopt(skcipher->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) { + wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s", + __func__, strerror(errno)); + goto fail; + } + + skcipher->t = accept(skcipher->s, NULL, NULL); + if (skcipher->t < 0) { + wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s", + __func__, strerror(errno)); + goto fail; + } + + return skcipher; +fail: + linux_af_alg_skcipher_deinit(skcipher); + return NULL; +} + + +static int linux_af_alg_skcipher_oper(struct linux_af_alg_skcipher *skcipher, + int enc, const u8 *in, u8 *out) +{ + char buf[CMSG_SPACE(sizeof(u32))]; + struct iovec io[1]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + u32 *op; + + io[0].iov_base = (void *) in; + io[0].iov_len = AES_BLOCK_SIZE; + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(u32)); + msg.msg_iov = io; + msg.msg_iovlen = 1; + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_OP; + hdr->cmsg_len = CMSG_LEN(sizeof(u32)); + op = (u32 *) CMSG_DATA(hdr); + *op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT; + + ret = sendmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + return -1; + } + + ret = read(skcipher->t, out, AES_BLOCK_SIZE); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: read failed: %s", + __func__, strerror(errno)); + return -1; + } + if (ret < AES_BLOCK_SIZE) { + wpa_printf(MSG_ERROR, + "%s: read did not return full data (%d/%d)", + __func__, (int) ret, AES_BLOCK_SIZE); + return -1; + } + + return 0; +} + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + return linux_af_alg_skcipher("ecb(aes)", key, len); +} + + +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + struct linux_af_alg_skcipher *skcipher = ctx; + + return linux_af_alg_skcipher_oper(skcipher, 1, plain, crypt); +} + + +void aes_encrypt_deinit(void *ctx) +{ + linux_af_alg_skcipher_deinit(ctx); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + return linux_af_alg_skcipher("ecb(aes)", key, len); +} + + +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + struct linux_af_alg_skcipher *skcipher = ctx; + + return linux_af_alg_skcipher_oper(skcipher, 0, crypt, plain); +} + + +void aes_decrypt_deinit(void *ctx) +{ + linux_af_alg_skcipher_deinit(ctx); +} + + +int rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len) +{ + struct linux_af_alg_skcipher *skcipher; + u8 *skip_buf; + char buf[CMSG_SPACE(sizeof(u32))]; + struct iovec io[2]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + u32 *op; + + skip_buf = os_zalloc(skip + 1); + if (!skip_buf) + return -1; + skcipher = linux_af_alg_skcipher("ecb(arc4)", key, keylen); + if (!skcipher) { + os_free(skip_buf); + return -1; + } + + io[0].iov_base = skip_buf; + io[0].iov_len = skip; + io[1].iov_base = data; + io[1].iov_len = data_len; + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(u32)); + msg.msg_iov = io; + msg.msg_iovlen = 2; + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_OP; + hdr->cmsg_len = CMSG_LEN(sizeof(u32)); + op = (u32 *) CMSG_DATA(hdr); + *op = ALG_OP_ENCRYPT; + + ret = sendmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + os_free(skip_buf); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + os_free(skip_buf); + + msg.msg_control = NULL; + msg.msg_controllen = 0; + ret = recvmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s", + __func__, strerror(errno)); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + linux_af_alg_skcipher_deinit(skcipher); + + if ((size_t) ret < skip + data_len) { + wpa_printf(MSG_ERROR, + "%s: recvmsg did not return full data (%d/%d)", + __func__, (int) ret, (int) (skip + data_len)); + return -1; + } + + return 0; +} + + +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + u8 pkey[8], next, tmp; + int i; + struct linux_af_alg_skcipher *skcipher; + char buf[CMSG_SPACE(sizeof(u32))]; + struct iovec io[1]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + u32 *op; + int res = -1; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + skcipher = linux_af_alg_skcipher("ecb(des)", pkey, sizeof(pkey)); + if (!skcipher) + goto fail; + + io[0].iov_base = (void *) clear; + io[0].iov_len = 8; + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(u32)); + msg.msg_iov = io; + msg.msg_iovlen = 1; + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_OP; + hdr->cmsg_len = CMSG_LEN(sizeof(u32)); + op = (u32 *) CMSG_DATA(hdr); + *op = ALG_OP_ENCRYPT; + + ret = sendmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + goto fail; + } + + ret = read(skcipher->t, cypher, 8); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: read failed: %s", + __func__, strerror(errno)); + goto fail; + } + if (ret < 8) { + wpa_printf(MSG_ERROR, + "%s: read did not return full data (%d/8)", + __func__, (int) ret); + goto fail; + } + + res = 0; +fail: + linux_af_alg_skcipher_deinit(skcipher); + return res; +} + + +static int aes_128_cbc_oper(const u8 *key, int enc, const u8 *iv, + u8 *data, size_t data_len) +{ + struct linux_af_alg_skcipher *skcipher; + char buf[100]; + struct iovec io[1]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + u32 *op; + struct af_alg_iv *alg_iv; + size_t iv_len = AES_BLOCK_SIZE; + + skcipher = linux_af_alg_skcipher("cbc(aes)", key, 16); + if (!skcipher) + return -1; + + io[0].iov_base = (void *) data; + io[0].iov_len = data_len; + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(u32)) + + CMSG_SPACE(sizeof(*alg_iv) + iv_len); + msg.msg_iov = io; + msg.msg_iovlen = 1; + + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_OP; + hdr->cmsg_len = CMSG_LEN(sizeof(u32)); + op = (u32 *) CMSG_DATA(hdr); + *op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT; + + hdr = CMSG_NXTHDR(&msg, hdr); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_IV; + hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len); + alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr); + alg_iv->ivlen = iv_len; + os_memcpy(alg_iv->iv, iv, iv_len); + + ret = sendmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + + ret = recvmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s", + __func__, strerror(errno)); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + if ((size_t) ret < data_len) { + wpa_printf(MSG_ERROR, + "%s: recvmsg not return full data (%d/%d)", + __func__, (int) ret, (int) data_len); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + + linux_af_alg_skcipher_deinit(skcipher); + return 0; +} + + +int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + return aes_128_cbc_oper(key, 1, iv, data, data_len); +} + + +int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + return aes_128_cbc_oper(key, 0, iv, data, data_len); +} + + +int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("cmac(aes)", key, key_len, num_elem, + addr, len, mac, AES_BLOCK_SIZE); +} + + +int omac1_aes_128_vector(const u8 *key, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return omac1_aes_vector(key, 16, num_elem, addr, len, mac); +} + + +int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_128_vector(key, 1, &data, &data_len, mac); +} + + +int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_vector(key, 32, 1, &data, &data_len, mac); +} + + +int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, + u8 *plain) +{ + struct linux_af_alg_skcipher *skcipher; + char buf[100]; + struct iovec io[1]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + u32 *op; + struct af_alg_iv *alg_iv; + size_t iv_len = 8; + + skcipher = linux_af_alg_skcipher("kw(aes)", kek, kek_len); + if (!skcipher) + return -1; + + io[0].iov_base = (void *) (cipher + iv_len); + io[0].iov_len = n * 8; + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(u32)) + + CMSG_SPACE(sizeof(*alg_iv) + iv_len); + msg.msg_iov = io; + msg.msg_iovlen = 1; + + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_OP; + hdr->cmsg_len = CMSG_LEN(sizeof(u32)); + op = (u32 *) CMSG_DATA(hdr); + *op = ALG_OP_DECRYPT; + + hdr = CMSG_NXTHDR(&msg, hdr); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_IV; + hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len); + alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr); + alg_iv->ivlen = iv_len; + os_memcpy(alg_iv->iv, cipher, iv_len); + + ret = sendmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + return -1; + } + + ret = read(skcipher->t, plain, n * 8); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: read failed: %s", + __func__, strerror(errno)); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + if (ret < n * 8) { + wpa_printf(MSG_ERROR, + "%s: read not return full data (%d/%d)", + __func__, (int) ret, n * 8); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + + linux_af_alg_skcipher_deinit(skcipher); + return 0; +} + + +struct crypto_cipher { + struct linux_af_alg_skcipher *skcipher; +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + const char *name; + struct af_alg_iv *alg_iv; + size_t iv_len = 0; + char buf[100]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + + ctx = os_zalloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + switch (alg) { + case CRYPTO_CIPHER_ALG_RC4: + name = "ecb(arc4)"; + break; + case CRYPTO_CIPHER_ALG_AES: + name = "cbc(aes)"; + iv_len = AES_BLOCK_SIZE; + break; + case CRYPTO_CIPHER_ALG_3DES: + name = "cbc(des3_ede)"; + iv_len = 8; + break; + case CRYPTO_CIPHER_ALG_DES: + name = "cbc(des)"; + iv_len = 8; + break; + default: + os_free(ctx); + return NULL; + } + + ctx->skcipher = linux_af_alg_skcipher(name, key, key_len); + if (!ctx->skcipher) { + os_free(ctx); + return NULL; + } + + if (iv && iv_len) { + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(*alg_iv) + iv_len); + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_IV; + hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len); + alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr); + alg_iv->ivlen = iv_len; + os_memcpy(alg_iv->iv, iv, iv_len); + + ret = sendmsg(ctx->skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + linux_af_alg_skcipher_deinit(ctx->skcipher); + os_free(ctx); + return NULL; + } + } + + return ctx; +} + + +static int crypto_cipher_oper(struct crypto_cipher *ctx, u32 type, const u8 *in, + u8 *out, size_t len) +{ + char buf[CMSG_SPACE(sizeof(u32))]; + struct iovec io[1]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + u32 *op; + + io[0].iov_base = (void *) in; + io[0].iov_len = len; + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(u32)); + msg.msg_iov = io; + msg.msg_iovlen = 1; + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_OP; + hdr->cmsg_len = CMSG_LEN(sizeof(u32)); + op = (u32 *) CMSG_DATA(hdr); + *op = type; + + ret = sendmsg(ctx->skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + return -1; + } + + ret = read(ctx->skcipher->t, out, len); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: read failed: %s", + __func__, strerror(errno)); + return -1; + } + if (ret < (ssize_t) len) { + wpa_printf(MSG_ERROR, + "%s: read did not return full data (%d/%d)", + __func__, (int) ret, (int) len); + return -1; + } + + return 0; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + return crypto_cipher_oper(ctx, ALG_OP_ENCRYPT, plain, crypt, len); +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + return crypto_cipher_oper(ctx, ALG_OP_DECRYPT, crypt, plain, len); +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + if (ctx) { + linux_af_alg_skcipher_deinit(ctx->skcipher); + os_free(ctx); + } +} + + +int crypto_global_init(void) +{ + return 0; +} + + +void crypto_global_deinit(void) +{ +} diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c index ffd23942e32d..1cc73d8ec16e 100644 --- a/src/crypto/crypto_module_tests.c +++ b/src/crypto/crypto_module_tests.c @@ -17,6 +17,7 @@ #include "crypto/crypto.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/sha384.h" static int test_siv(void) @@ -92,7 +93,7 @@ static int test_siv(void) addr[0] = ad; len[0] = sizeof(ad); - if (aes_siv_encrypt(key, plaintext, sizeof(plaintext), + if (aes_siv_encrypt(key, sizeof(key), plaintext, sizeof(plaintext), 1, addr, len, out)) { wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed"); return 1; @@ -103,7 +104,8 @@ static int test_siv(void) return 1; } - if (aes_siv_decrypt(key, iv_c, sizeof(iv_c), 1, addr, len, out)) { + if (aes_siv_decrypt(key, sizeof(key), iv_c, sizeof(iv_c), + 1, addr, len, out)) { wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed"); return 1; } @@ -121,7 +123,8 @@ static int test_siv(void) addr[2] = nonce_2; len[2] = sizeof(nonce_2); - if (aes_siv_encrypt(key_2, plaintext_2, sizeof(plaintext_2), + if (aes_siv_encrypt(key_2, sizeof(key_2), + plaintext_2, sizeof(plaintext_2), 3, addr, len, out)) { wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed"); return 1; @@ -132,7 +135,8 @@ static int test_siv(void) return 1; } - if (aes_siv_decrypt(key_2, iv_c_2, sizeof(iv_c_2), 3, addr, len, out)) { + if (aes_siv_decrypt(key_2, sizeof(key_2), iv_c_2, sizeof(iv_c_2), + 3, addr, len, out)) { wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed"); return 1; } @@ -1292,13 +1296,14 @@ static const struct { }; static const struct hmac_test { - u8 key[80]; + u8 key[150]; size_t key_len; - u8 data[128]; + u8 data[160]; size_t data_len; - u8 hash[32]; + u8 hash[32]; /* HMAC-SHA-256 */ + u8 hash384[48]; /* HMAC-SHA-384 */ } hmac_tests[] = { - /* draft-ietf-ipsec-ciph-sha-256-01.txt */ + /* draft-ietf-ipsec-ciph-sha-256-01.txt; RFC 4231 */ { { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, @@ -1313,7 +1318,8 @@ static const struct hmac_test { 0x4d, 0xd9, 0x39, 0x75, 0x0f, 0x7a, 0x06, 0x6a, 0x7f, 0x98, 0xcc, 0x13, 0x1c, 0xb1, 0x6a, 0x66, 0x92, 0x75, 0x90, 0x21, 0xcf, 0xab, 0x81, 0x81 - } + }, + { } }, { { @@ -1330,7 +1336,8 @@ static const struct hmac_test { 0x18, 0x4b, 0xa7, 0x31, 0x31, 0xc5, 0x3c, 0xae, 0xe6, 0x98, 0xe3, 0x61, 0x19, 0x42, 0x11, 0x49, 0xea, 0x8c, 0x71, 0x24, 0x56, 0x69, 0x7d, 0x30 - } + }, + { } }, { { @@ -1348,7 +1355,8 @@ static const struct hmac_test { 0xd3, 0xee, 0xb3, 0xe7, 0x73, 0xd9, 0x5a, 0xab, 0x73, 0xac, 0xf0, 0xfd, 0x06, 0x04, 0x47, 0xa5, 0xeb, 0x45, 0x95, 0xbf, 0x33, 0xa9, 0xd1, 0xa3 - } + }, + { } }, { { @@ -1365,9 +1373,34 @@ static const struct hmac_test { 0x99, 0x03, 0xa0, 0xf1, 0xcf, 0x2b, 0xbd, 0xc5, 0xba, 0x0a, 0xa3, 0xf3, 0xd9, 0xae, 0x3c, 0x1c, 0x7a, 0x3b, 0x16, 0x96, 0xa0, 0xb6, 0x8c, 0xf7 + }, + { } + }, + { /* RFC 4231 - Test Case 1 */ + { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b + }, + 20, + "Hi There", + 8, + { + 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, + 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1, 0x2b, + 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7, + 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7 + }, + { + 0xaf, 0xd0, 0x39, 0x44, 0xd8, 0x48, 0x95, 0x62, + 0x6b, 0x08, 0x25, 0xf4, 0xab, 0x46, 0x90, 0x7f, + 0x15, 0xf9, 0xda, 0xdb, 0xe4, 0x10, 0x1e, 0xc6, + 0x82, 0xaa, 0x03, 0x4c, 0x7c, 0xeb, 0xc5, 0x9c, + 0xfa, 0xea, 0x9e, 0xa9, 0x07, 0x6e, 0xde, 0x7f, + 0x4a, 0xf1, 0x52, 0xe8, 0xb2, 0xfa, 0x9c, 0xb6 } }, - { + { /* RFC 4231 - Test Case 2 */ "Jefe", 4, "what do ya want for nothing?", @@ -1377,6 +1410,14 @@ static const struct hmac_test { 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7, 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43 + }, + { + 0xaf, 0x45, 0xd2, 0xe3, 0x76, 0x48, 0x40, 0x31, + 0x61, 0x7f, 0x78, 0xd2, 0xb5, 0x8a, 0x6b, 0x1b, + 0x9c, 0x7e, 0xf4, 0x64, 0xf5, 0xa0, 0x1b, 0x47, + 0xe4, 0x2e, 0xc3, 0x73, 0x63, 0x22, 0x44, 0x5e, + 0x8e, 0x22, 0x40, 0xca, 0x5e, 0x69, 0xe2, 0xc7, + 0x8b, 0x32, 0x39, 0xec, 0xfa, 0xb2, 0x16, 0x49 } }, { @@ -1402,6 +1443,39 @@ static const struct hmac_test { 0x91, 0xe5, 0x3a, 0xba, 0x30, 0x92, 0xf9, 0x62, 0xe5, 0x49, 0xfe, 0x6c, 0xe9, 0xed, 0x7f, 0xdc, 0x43, 0x19, 0x1f, 0xbd, 0xe4, 0x5c, 0x30, 0xb0 + }, + { } + }, + { /* RFC 4231 - Test Case 3 */ + { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa + }, + 20, + { + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd + }, + 50, + { + 0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, + 0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91, 0x81, 0xa7, + 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22, + 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe + }, + { + 0x88, 0x06, 0x26, 0x08, 0xd3, 0xe6, 0xad, 0x8a, + 0x0a, 0xa2, 0xac, 0xe0, 0x14, 0xc8, 0xa8, 0x6f, + 0x0a, 0xa6, 0x35, 0xd9, 0x47, 0xac, 0x9f, 0xeb, + 0xe8, 0x3e, 0xf4, 0xe5, 0x59, 0x66, 0x14, 0x4b, + 0x2a, 0x5a, 0xb3, 0x9d, 0xc1, 0x38, 0x14, 0xb9, + 0x4e, 0x3a, 0xb6, 0xe1, 0x01, 0xa3, 0x4f, 0x27 } }, { @@ -1428,6 +1502,40 @@ static const struct hmac_test { 0x4c, 0x66, 0xde, 0xe0, 0xf8, 0xf0, 0x74, 0x55, 0x6e, 0xc4, 0xaf, 0x55, 0xef, 0x07, 0x99, 0x85, 0x41, 0x46, 0x8e, 0xb4, 0x9b, 0xd2, 0xe9, 0x17 + }, + { } + }, + { /* RFC 4231 - Test Case 4 */ + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, + }, + 25, + { + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd + }, + 50, + { + 0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e, + 0xa4, 0xcc, 0x81, 0x98, 0x99, 0xf2, 0x08, 0x3a, + 0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, 0xf8, 0x07, + 0x7a, 0x2e, 0x3f, 0xf4, 0x67, 0x29, 0x66, 0x5b + }, + { + 0x3e, 0x8a, 0x69, 0xb7, 0x78, 0x3c, 0x25, 0x85, + 0x19, 0x33, 0xab, 0x62, 0x90, 0xaf, 0x6c, 0xa7, + 0x7a, 0x99, 0x81, 0x48, 0x08, 0x50, 0x00, 0x9c, + 0xc5, 0x57, 0x7c, 0x6e, 0x1f, 0x57, 0x3b, 0x4e, + 0x68, 0x01, 0xdd, 0x23, 0xc4, 0xa7, 0xd6, 0x79, + 0xcc, 0xf8, 0xa3, 0x86, 0xc6, 0x74, 0xcf, 0xfb } }, { @@ -1445,7 +1553,8 @@ static const struct hmac_test { 0x1a, 0xb9, 0xc3, 0x74, 0x9a, 0x5f, 0x1c, 0x17, 0xd4, 0xf5, 0x89, 0x66, 0x8a, 0x58, 0x7b, 0x27, 0x00, 0xa9, 0xc9, 0x7c, 0x11, 0x93, 0xcf, 0x42 - } + }, + { } }, { { @@ -1468,6 +1577,45 @@ static const struct hmac_test { 0xf8, 0x0a, 0x96, 0xf7, 0x8e, 0x65, 0x38, 0xdb, 0xe2, 0xe7, 0xb8, 0x20, 0xe3, 0xdd, 0x97, 0x0e, 0x7d, 0xdd, 0x39, 0x09, 0x1b, 0x32, 0x35, 0x2f + }, + { } + }, + { /* RFC 4231 - Test Case 6 */ + { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa + }, + 131, + "Test Using Larger Than Block-Size Key - Hash Key First", + 54, + { + 0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, + 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, 0xb7, 0x7f, + 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14, + 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54 + }, + { + 0x4e, 0xce, 0x08, 0x44, 0x85, 0x81, 0x3e, 0x90, + 0x88, 0xd2, 0xc6, 0x3a, 0x04, 0x1b, 0xc5, 0xb4, + 0x4f, 0x9e, 0xf1, 0x01, 0x2a, 0x2b, 0x58, 0x8f, + 0x3c, 0xd1, 0x1f, 0x05, 0x03, 0x3a, 0xc4, 0xc6, + 0x0c, 0x2e, 0xf6, 0xab, 0x40, 0x30, 0xfe, 0x82, + 0x96, 0x24, 0x8d, 0xf1, 0x63, 0xf4, 0x49, 0x52 } }, { @@ -1492,6 +1640,45 @@ static const struct hmac_test { 0xc8, 0x48, 0x1a, 0x5c, 0xa4, 0x82, 0x5b, 0xc8, 0x84, 0xd3, 0xe7, 0xa1, 0xff, 0x98, 0xa2, 0xfc, 0x2a, 0xc7, 0xd8, 0xe0, 0x64, 0xc3, 0xb2, 0xe6 + }, + { } + }, + { /* RFC 4231 - Test Case 7 */ + { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa + }, + 131, + "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.", + 152, + { + 0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, + 0x27, 0x63, 0x5f, 0xbc, 0xd5, 0xb0, 0xe9, 0x44, + 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93, + 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2 + }, + { + 0x66, 0x17, 0x17, 0x8e, 0x94, 0x1f, 0x02, 0x0d, + 0x35, 0x1e, 0x2f, 0x25, 0x4e, 0x8f, 0xd3, 0x2c, + 0x60, 0x24, 0x20, 0xfe, 0xb0, 0xb8, 0xfb, 0x9a, + 0xdc, 0xce, 0xbb, 0x82, 0x46, 0x1e, 0x99, 0xc5, + 0xa6, 0x78, 0xcc, 0x31, 0xe7, 0x99, 0x17, 0x6d, + 0x38, 0x60, 0xe6, 0x11, 0x0c, 0x46, 0x52, 0x3e } } }; @@ -1606,6 +1793,96 @@ static int test_sha256(void) } +static int test_sha384(void) +{ +#ifdef CONFIG_SHA384 + unsigned int i; + u8 hash[48]; + const u8 *addr[2]; + size_t len[2]; + int errors = 0; + const char *data = "hello"; + const u8 hash_res[] = { + 0x59, 0xe1, 0x74, 0x87, 0x77, 0x44, 0x8c, 0x69, + 0xde, 0x6b, 0x80, 0x0d, 0x7a, 0x33, 0xbb, 0xfb, + 0x9f, 0xf1, 0xb4, 0x63, 0xe4, 0x43, 0x54, 0xc3, + 0x55, 0x3b, 0xcd, 0xb9, 0xc6, 0x66, 0xfa, 0x90, + 0x12, 0x5a, 0x3c, 0x79, 0xf9, 0x03, 0x97, 0xbd, + 0xf5, 0xf6, 0xa1, 0x3d, 0xe8, 0x28, 0x68, 0x4f + }; + + addr[0] = (const u8 *) data; + len[0] = 5; + if (sha384_vector(1, addr, len, hash) < 0 || + os_memcmp(hash, hash_res, 48) != 0) { + wpa_printf(MSG_INFO, "SHA384 test case 1: FAIL"); + errors++; + } else { + wpa_printf(MSG_INFO, "SHA384 test case 1: OK"); + } + + addr[0] = (const u8 *) data; + len[0] = 4; + addr[1] = (const u8 *) data + 4; + len[1] = 1; + if (sha384_vector(2, addr, len, hash) < 0 || + os_memcmp(hash, hash_res, 48) != 0) { + wpa_printf(MSG_INFO, "SHA384 test case 2: FAIL"); + errors++; + } else { + wpa_printf(MSG_INFO, "SHA384 test case 2: OK"); + } + + for (i = 0; i < ARRAY_SIZE(hmac_tests); i++) { + const struct hmac_test *t = &hmac_tests[i]; + + if (t->hash384[0] == 0 && t->hash384[1] == 0 && + t->hash384[2] == 0 && t->hash384[3] == 0) + continue; + wpa_printf(MSG_INFO, "HMAC-SHA384 test case %d:", i + 1); + + if (hmac_sha384(t->key, t->key_len, t->data, t->data_len, + hash) < 0 || + os_memcmp(hash, t->hash384, 48) != 0) { + wpa_printf(MSG_INFO, " FAIL"); + errors++; + } else + wpa_printf(MSG_INFO, " OK"); + + addr[0] = t->data; + len[0] = t->data_len; + if (hmac_sha384_vector(t->key, t->key_len, 1, addr, len, + hash) < 0 || + os_memcmp(hash, t->hash384, 48) != 0) { + wpa_printf(MSG_INFO, " FAIL"); + errors++; + } else + wpa_printf(MSG_INFO, " OK"); + + if (len[0]) { + addr[0] = t->data; + len[0] = 1; + addr[1] = t->data + 1; + len[1] = t->data_len - 1; + if (hmac_sha384_vector(t->key, t->key_len, 2, addr, len, + hash) < 0 || + os_memcmp(hash, t->hash384, 48) != 0) { + wpa_printf(MSG_INFO, " FAIL"); + errors++; + } else + wpa_printf(MSG_INFO, " OK"); + } + } + + if (!errors) + wpa_printf(MSG_INFO, "SHA384 test cases passed"); + return errors; +#else /* CONFIG_SHA384 */ + return 0; +#endif /* CONFIG_SHA384 */ +} + + static int test_fips186_2_prf(void) { /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */ @@ -1635,6 +1912,135 @@ static int test_fips186_2_prf(void) } +static int test_extract_expand_hkdf(void) +{ + u8 prk[SHA256_MAC_LEN]; + u8 okm[82]; + + /* RFC 5869, A.1 */ + u8 ikm1[22] = { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b + }; + u8 salt1[13] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c + }; + u8 info1[10] = { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9 + }; + u8 prk1[32] = { + 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, + 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, + 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31, + 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5 + }; + u8 okm1[42] = { + 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, + 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, + 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, + 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, + 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, + 0x58, 0x65 + }; + + /* RFC 5869, A.2 */ + u8 ikm2[80] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f + }; + u8 salt2[80] = { + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf + }; + u8 info2[80] = { + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff + }; + u8 prk2[32] = { + 0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a, + 0x06, 0x10, 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c, + 0xef, 0x76, 0x00, 0x14, 0x90, 0x46, 0x71, 0x01, + 0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, 0xc2, 0x44 + }; + u8 okm2[82] = { + 0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, + 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34, + 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8, + 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c, + 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, + 0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09, + 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8, + 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, + 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87, + 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f, + 0x1d, 0x87 + }; + + wpa_printf(MSG_INFO, "Testing Extract-and-Expand HKDF (RFC 5869)"); + + wpa_printf(MSG_INFO, "RFC 5869 - Test Case 1"); + if (hmac_sha256(salt1, sizeof(salt1), ikm1, sizeof(ikm1), prk) < 0) + return -1; + if (os_memcmp(prk, prk1, SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, "HKDF-Extract mismatch in PRK"); + return -1; + } + if (hmac_sha256_kdf(prk1, sizeof(prk1), NULL, info1, sizeof(info1), + okm, sizeof(okm1)) < 0) + return -1; + if (os_memcmp(okm, okm1, sizeof(okm1)) != 0) { + wpa_printf(MSG_INFO, "HKDF-Expand mismatch in OKM"); + return -1; + } + + wpa_printf(MSG_INFO, "RFC 5869 - Test Case 2"); + if (hmac_sha256(salt2, sizeof(salt2), ikm2, sizeof(ikm2), prk) < 0) + return -1; + if (os_memcmp(prk, prk2, SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, "HKDF-Extract mismatch in PRK"); + return -1; + } + if (hmac_sha256_kdf(prk2, sizeof(prk2), NULL, info2, sizeof(info2), + okm, sizeof(okm2)) < 0) + return -1; + if (os_memcmp(okm, okm2, sizeof(okm2)) != 0) { + wpa_printf(MSG_INFO, "HKDF-Expand mismatch in OKM"); + return -1; + } + + wpa_printf(MSG_INFO, "Extract-and-Expand HKDF test cases passed"); + + return 0; +} + + static int test_ms_funcs(void) { #ifndef CONFIG_FIPS @@ -1751,7 +2157,9 @@ int crypto_module_tests(void) test_md5() || test_sha1() || test_sha256() || + test_sha384() || test_fips186_2_prf() || + test_extract_expand_hkdf() || test_ms_funcs()) ret = -1; diff --git a/src/crypto/crypto_nettle.c b/src/crypto/crypto_nettle.c new file mode 100644 index 000000000000..4e31bc801132 --- /dev/null +++ b/src/crypto/crypto_nettle.c @@ -0,0 +1,437 @@ +/* + * Wrapper functions for libnettle and libgmp + * Copyright (c) 2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include <nettle/nettle-meta.h> +#include <nettle/des.h> +#undef des_encrypt +#include <nettle/hmac.h> +#include <nettle/aes.h> +#undef aes_encrypt +#undef aes_decrypt +#include <nettle/arcfour.h> +#include <nettle/bignum.h> + +#include "common.h" +#include "md5.h" +#include "sha1.h" +#include "sha256.h" +#include "sha384.h" +#include "sha512.h" +#include "crypto.h" + + +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + struct des_ctx ctx; + u8 pkey[8], next, tmp; + int i; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + nettle_des_set_key(&ctx, pkey); + nettle_des_encrypt(&ctx, DES_BLOCK_SIZE, cypher, clear); + os_memset(&ctx, 0, sizeof(ctx)); + return 0; +} + + +static int nettle_digest_vector(const struct nettle_hash *alg, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + void *ctx; + size_t i; + + if (TEST_FAIL()) + return -1; + + ctx = os_malloc(alg->context_size); + if (!ctx) + return -1; + alg->init(ctx); + for (i = 0; i < num_elem; i++) + alg->update(ctx, len[i], addr[i]); + alg->digest(ctx, alg->digest_size, mac); + bin_clear_free(ctx, alg->context_size); + return 0; +} + + +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nettle_digest_vector(&nettle_md4, num_elem, addr, len, mac); +} + + +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nettle_digest_vector(&nettle_md5, num_elem, addr, len, mac); +} + + +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nettle_digest_vector(&nettle_sha1, num_elem, addr, len, mac); +} + + +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nettle_digest_vector(&nettle_sha256, num_elem, addr, len, mac); +} + + +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nettle_digest_vector(&nettle_sha384, num_elem, addr, len, mac); +} + + +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nettle_digest_vector(&nettle_sha512, num_elem, addr, len, mac); +} + + +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + struct hmac_md5_ctx ctx; + size_t i; + + if (TEST_FAIL()) + return -1; + + hmac_md5_set_key(&ctx, key_len, key); + for (i = 0; i < num_elem; i++) + hmac_md5_update(&ctx, len[i], addr[i]); + hmac_md5_digest(&ctx, MD5_DIGEST_SIZE, mac); + os_memset(&ctx, 0, sizeof(ctx)); + return 0; +} + + +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} + + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + struct hmac_sha1_ctx ctx; + size_t i; + + if (TEST_FAIL()) + return -1; + + hmac_sha1_set_key(&ctx, key_len, key); + for (i = 0; i < num_elem; i++) + hmac_sha1_update(&ctx, len[i], addr[i]); + hmac_sha1_digest(&ctx, SHA1_DIGEST_SIZE, mac); + os_memset(&ctx, 0, sizeof(ctx)); + return 0; +} + + +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + + +#ifdef CONFIG_SHA256 + +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + struct hmac_sha256_ctx ctx; + size_t i; + + if (TEST_FAIL()) + return -1; + + hmac_sha256_set_key(&ctx, key_len, key); + for (i = 0; i < num_elem; i++) + hmac_sha256_update(&ctx, len[i], addr[i]); + hmac_sha256_digest(&ctx, SHA256_DIGEST_SIZE, mac); + os_memset(&ctx, 0, sizeof(ctx)); + return 0; +} + + +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA256 */ + + +#ifdef CONFIG_SHA384 + +int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + struct hmac_sha384_ctx ctx; + size_t i; + + if (TEST_FAIL()) + return -1; + + hmac_sha384_set_key(&ctx, key_len, key); + for (i = 0; i < num_elem; i++) + hmac_sha384_update(&ctx, len[i], addr[i]); + hmac_sha384_digest(&ctx, SHA384_DIGEST_SIZE, mac); + os_memset(&ctx, 0, sizeof(ctx)); + return 0; +} + + +int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA384 */ + + +#ifdef CONFIG_SHA512 + +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + struct hmac_sha512_ctx ctx; + size_t i; + + if (TEST_FAIL()) + return -1; + + hmac_sha512_set_key(&ctx, key_len, key); + for (i = 0; i < num_elem; i++) + hmac_sha512_update(&ctx, len[i], addr[i]); + hmac_sha512_digest(&ctx, SHA512_DIGEST_SIZE, mac); + os_memset(&ctx, 0, sizeof(ctx)); + return 0; +} + + +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA512 */ + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + struct aes_ctx *ctx; + + if (TEST_FAIL()) + return NULL; + ctx = os_malloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + nettle_aes_set_encrypt_key(ctx, len, key); + + return ctx; +} + + +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + struct aes_ctx *actx = ctx; + nettle_aes_encrypt(actx, AES_BLOCK_SIZE, crypt, plain); + return 0; +} + + +void aes_encrypt_deinit(void *ctx) +{ + struct aes_ctx *actx = ctx; + bin_clear_free(actx, sizeof(*actx)); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + struct aes_ctx *ctx; + + if (TEST_FAIL()) + return NULL; + ctx = os_malloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + nettle_aes_set_decrypt_key(ctx, len, key); + + return ctx; +} + + +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + struct aes_ctx *actx = ctx; + nettle_aes_decrypt(actx, AES_BLOCK_SIZE, plain, crypt); + return 0; +} + + +void aes_decrypt_deinit(void *ctx) +{ + struct aes_ctx *actx = ctx; + bin_clear_free(actx, sizeof(*actx)); +} + + +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) +{ + size_t pubkey_len, pad; + + if (os_get_random(privkey, prime_len) < 0) + return -1; + if (os_memcmp(privkey, prime, prime_len) > 0) { + /* Make sure private value is smaller than prime */ + privkey[0] = 0; + } + + pubkey_len = prime_len; + if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len, + pubkey, &pubkey_len) < 0) + return -1; + if (pubkey_len < prime_len) { + pad = prime_len - pubkey_len; + os_memmove(pubkey + pad, pubkey, pubkey_len); + os_memset(pubkey, 0, pad); + } + + return 0; +} + + +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) +{ + return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +} + + +int crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len) +{ + mpz_t bn_base, bn_exp, bn_modulus, bn_result; + int ret = -1; + size_t len; + + mpz_inits(bn_base, bn_exp, bn_modulus, bn_result, NULL); + mpz_import(bn_base, base_len, 1, 1, 1, 0, base); + mpz_import(bn_exp, power_len, 1, 1, 1, 0, power); + mpz_import(bn_modulus, modulus_len, 1, 1, 1, 0, modulus); + + mpz_powm(bn_result, bn_base, bn_exp, bn_modulus); + len = mpz_sizeinbase(bn_result, 2); + len = (len + 7) / 8; + if (*result_len < len) + goto error; + mpz_export(result, result_len, 1, 1, 1, 0, bn_result); + ret = 0; + +error: + mpz_clears(bn_base, bn_exp, bn_modulus, bn_result, NULL); + return ret; +} + + +struct crypto_cipher { + enum crypto_cipher_alg alg; + union { + struct arcfour_ctx arcfour_ctx; + } u; +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + ctx->alg = alg; + + switch (alg) { + case CRYPTO_CIPHER_ALG_RC4: + nettle_arcfour_set_key(&ctx->u.arcfour_ctx, key_len, key); + break; + default: + os_free(ctx); + return NULL; + } + + return ctx; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + switch (ctx->alg) { + case CRYPTO_CIPHER_ALG_RC4: + nettle_arcfour_crypt(&ctx->u.arcfour_ctx, len, crypt, plain); + break; + default: + return -1; + } + + return 0; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + switch (ctx->alg) { + case CRYPTO_CIPHER_ALG_RC4: + nettle_arcfour_crypt(&ctx->u.arcfour_ctx, len, plain, crypt); + break; + default: + return -1; + } + + return 0; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + bin_clear_free(ctx, sizeof(*ctx)); +} diff --git a/src/crypto/crypto_none.c b/src/crypto/crypto_none.c index 011f3f35a055..547919418af9 100644 --- a/src/crypto/crypto_none.c +++ b/src/crypto/crypto_none.c @@ -18,6 +18,7 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) } -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { + return 0; } diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index 19e0e2be87be..f89053a89d67 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -1,6 +1,6 @@ /* * Wrapper functions for OpenSSL libcrypto - * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -29,11 +29,14 @@ #include "sha1.h" #include "sha256.h" #include "sha384.h" +#include "sha512.h" #include "md5.h" #include "aes_wrap.h" #include "crypto.h" -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) /* Compatibility wrappers for older versions. */ static HMAC_CTX * HMAC_CTX_new(void) @@ -79,7 +82,9 @@ static void EVP_MD_CTX_free(EVP_MD_CTX *ctx) static BIGNUM * get_group5_prime(void) { -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !(defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) return BN_get_rfc3526_prime_1536(NULL); #elif !defined(OPENSSL_IS_BORINGSSL) return get_rfc3526_prime_1536(NULL); @@ -109,6 +114,9 @@ static BIGNUM * get_group5_prime(void) #ifdef OPENSSL_NO_SHA256 #define NO_SHA256_WRAPPER #endif +#ifdef OPENSSL_NO_SHA512 +#define NO_SHA384_WRAPPER +#endif static int openssl_digest_vector(const EVP_MD *type, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) @@ -158,7 +166,7 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) #endif /* CONFIG_FIPS */ -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { u8 pkey[8], next, tmp; int i; @@ -176,6 +184,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) DES_set_key((DES_cblock *) &pkey, &ks); DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks, DES_ENCRYPT); + return 0; } @@ -243,15 +252,31 @@ int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, #endif /* NO_SHA256_WRAPPER */ +#ifndef NO_SHA384_WRAPPER +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return openssl_digest_vector(EVP_sha384(), num_elem, addr, len, mac); +} +#endif /* NO_SHA384_WRAPPER */ + + +#ifndef NO_SHA512_WRAPPER +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return openssl_digest_vector(EVP_sha512(), num_elem, addr, len, mac); +} +#endif /* NO_SHA512_WRAPPER */ + + static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen) { switch (keylen) { case 16: return EVP_aes_128_ecb(); -#ifndef OPENSSL_IS_BORINGSSL case 24: return EVP_aes_192_ecb(); -#endif /* OPENSSL_IS_BORINGSSL */ case 32: return EVP_aes_256_ecb(); } @@ -269,8 +294,11 @@ void * aes_encrypt_init(const u8 *key, size_t len) return NULL; type = aes_get_evp_cipher(len); - if (type == NULL) + if (!type) { + wpa_printf(MSG_INFO, "%s: Unsupported len=%u", + __func__, (unsigned int) len); return NULL; + } ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) @@ -284,14 +312,16 @@ void * aes_encrypt_init(const u8 *key, size_t len) } -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { EVP_CIPHER_CTX *c = ctx; int clen = 16; if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s", ERR_error_string(ERR_get_error(), NULL)); + return -1; } + return 0; } @@ -321,8 +351,11 @@ void * aes_decrypt_init(const u8 *key, size_t len) return NULL; type = aes_get_evp_cipher(len); - if (type == NULL) + if (!type) { + wpa_printf(MSG_INFO, "%s: Unsupported len=%u", + __func__, (unsigned int) len); return NULL; + } ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) @@ -336,14 +369,16 @@ void * aes_decrypt_init(const u8 *key, size_t len) } -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { EVP_CIPHER_CTX *c = ctx; int plen = 16; if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s", ERR_error_string(ERR_get_error(), NULL)); + return -1; } + return 0; } @@ -372,6 +407,8 @@ int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher) AES_KEY actx; int res; + if (TEST_FAIL()) + return -1; if (AES_set_encrypt_key(kek, kek_len << 3, &actx)) return -1; res = AES_wrap_key(&actx, NULL, cipher, plain, n * 8); @@ -386,6 +423,8 @@ int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, AES_KEY actx; int res; + if (TEST_FAIL()) + return -1; if (AES_set_decrypt_key(kek, kek_len << 3, &actx)) return -1; res = AES_unwrap_key(&actx, NULL, plain, cipher, (n + 1) * 8); @@ -452,6 +491,42 @@ int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) } +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) +{ + size_t pubkey_len, pad; + + if (os_get_random(privkey, prime_len) < 0) + return -1; + if (os_memcmp(privkey, prime, prime_len) > 0) { + /* Make sure private value is smaller than prime */ + privkey[0] = 0; + } + + pubkey_len = prime_len; + if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len, + pubkey, &pubkey_len) < 0) + return -1; + if (pubkey_len < prime_len) { + pad = prime_len - pubkey_len; + os_memmove(pubkey + pad, pubkey, pubkey_len); + os_memset(pubkey, 0, pad); + } + + return 0; +} + + +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) +{ + return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +} + + int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, @@ -611,7 +686,9 @@ void crypto_cipher_deinit(struct crypto_cipher *ctx) void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) DH *dh; struct wpabuf *pubkey = NULL, *privkey = NULL; size_t publen, privlen; @@ -712,7 +789,9 @@ err: void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) DH *dh; dh = DH_new(); @@ -1016,7 +1095,7 @@ int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return openssl_hmac_vector(EVP_sha384(), key, key_len, num_elem, addr, - len, mac, 32); + len, mac, 48); } @@ -1029,6 +1108,25 @@ int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, #endif /* CONFIG_SHA384 */ +#ifdef CONFIG_SHA512 + +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return openssl_hmac_vector(EVP_sha512(), key, key_len, num_elem, addr, + len, mac, 64); +} + + +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA512 */ + + int crypto_get_random(void *buf, size_t len) { if (RAND_bytes(buf, len) != 1) @@ -1150,6 +1248,12 @@ int crypto_bignum_to_bin(const struct crypto_bignum *a, } +int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m) +{ + return BN_rand_range((BIGNUM *) r, (const BIGNUM *) m) == 1 ? 0 : -1; +} + + int crypto_bignum_add(const struct crypto_bignum *a, const struct crypto_bignum *b, struct crypto_bignum *c) @@ -1275,6 +1379,15 @@ int crypto_bignum_mulmod(const struct crypto_bignum *a, } +int crypto_bignum_rshift(const struct crypto_bignum *a, int n, + struct crypto_bignum *r) +{ + /* Note: BN_rshift() does not modify the first argument even though it + * has not been marked const. */ + return BN_rshift((BIGNUM *) a, (BIGNUM *) r, n) == 1 ? 0 : -1; +} + + int crypto_bignum_cmp(const struct crypto_bignum *a, const struct crypto_bignum *b) { @@ -1300,6 +1413,12 @@ int crypto_bignum_is_one(const struct crypto_bignum *a) } +int crypto_bignum_is_odd(const struct crypto_bignum *a) +{ + return BN_is_odd((const BIGNUM *) a); +} + + int crypto_bignum_legendre(const struct crypto_bignum *a, const struct crypto_bignum *p) { @@ -1343,6 +1462,7 @@ fail: struct crypto_ec { EC_GROUP *group; + int nid; BN_CTX *bnctx; BIGNUM *prime; BIGNUM *order; @@ -1400,6 +1520,7 @@ struct crypto_ec * crypto_ec_init(int group) if (e == NULL) return NULL; + e->nid = nid; e->bnctx = BN_CTX_new(); e->group = EC_GROUP_new_by_curve_name(nid); e->prime = BN_new(); @@ -1432,6 +1553,13 @@ void crypto_ec_deinit(struct crypto_ec *e) } +int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor) +{ + return EC_GROUP_get_cofactor(e->group, (BIGNUM *) cofactor, + e->bnctx) == 0 ? -1 : 0; +} + + struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) { if (TEST_FAIL()) @@ -1454,6 +1582,12 @@ size_t crypto_ec_prime_len_bits(struct crypto_ec *e) } +size_t crypto_ec_order_len(struct crypto_ec *e) +{ + return BN_num_bytes(e->order); +} + + const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e) { return (const struct crypto_bignum *) e->prime; @@ -1475,6 +1609,16 @@ void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear) } +int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p, + struct crypto_bignum *x) +{ + return EC_POINT_get_affine_coordinates_GFp(e->group, + (const EC_POINT *) p, + (BIGNUM *) x, NULL, + e->bnctx) == 1 ? 0 : -1; +} + + int crypto_ec_point_to_bin(struct crypto_ec *e, const struct crypto_ec_point *point, u8 *x, u8 *y) { @@ -1640,4 +1784,228 @@ int crypto_ec_point_cmp(const struct crypto_ec *e, (const EC_POINT *) b, e->bnctx); } + +struct crypto_ecdh { + struct crypto_ec *ec; + EVP_PKEY *pkey; +}; + +struct crypto_ecdh * crypto_ecdh_init(int group) +{ + struct crypto_ecdh *ecdh; + EVP_PKEY *params = NULL; + EC_KEY *ec_params; + EVP_PKEY_CTX *kctx = NULL; + + ecdh = os_zalloc(sizeof(*ecdh)); + if (!ecdh) + goto fail; + + ecdh->ec = crypto_ec_init(group); + if (!ecdh->ec) + goto fail; + + ec_params = EC_KEY_new_by_curve_name(ecdh->ec->nid); + if (!ec_params) { + wpa_printf(MSG_ERROR, + "OpenSSL: Failed to generate EC_KEY parameters"); + goto fail; + } + EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE); + params = EVP_PKEY_new(); + if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: Failed to generate EVP_PKEY parameters"); + goto fail; + } + + kctx = EVP_PKEY_CTX_new(params, NULL); + if (!kctx) + goto fail; + + if (EVP_PKEY_keygen_init(kctx) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EVP_PKEY_keygen_init failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (EVP_PKEY_keygen(kctx, &ecdh->pkey) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_PKEY_keygen failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + +done: + EVP_PKEY_free(params); + EVP_PKEY_CTX_free(kctx); + + return ecdh; +fail: + crypto_ecdh_deinit(ecdh); + ecdh = NULL; + goto done; +} + + +struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y) +{ + struct wpabuf *buf = NULL; + EC_KEY *eckey; + const EC_POINT *pubkey; + BIGNUM *x, *y = NULL; + int len = BN_num_bytes(ecdh->ec->prime); + int res; + + eckey = EVP_PKEY_get1_EC_KEY(ecdh->pkey); + if (!eckey) + return NULL; + + pubkey = EC_KEY_get0_public_key(eckey); + if (!pubkey) + return NULL; + + x = BN_new(); + if (inc_y) { + y = BN_new(); + if (!y) + goto fail; + } + buf = wpabuf_alloc(inc_y ? 2 * len : len); + if (!x || !buf) + goto fail; + + if (EC_POINT_get_affine_coordinates_GFp(ecdh->ec->group, pubkey, + x, y, ecdh->ec->bnctx) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EC_POINT_get_affine_coordinates_GFp failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + res = crypto_bignum_to_bin((struct crypto_bignum *) x, + wpabuf_put(buf, len), len, len); + if (res < 0) + goto fail; + + if (inc_y) { + res = crypto_bignum_to_bin((struct crypto_bignum *) y, + wpabuf_put(buf, len), len, len); + if (res < 0) + goto fail; + } + +done: + BN_clear_free(x); + BN_clear_free(y); + EC_KEY_free(eckey); + + return buf; +fail: + wpabuf_free(buf); + buf = NULL; + goto done; +} + + +struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y, + const u8 *key, size_t len) +{ + BIGNUM *x, *y = NULL; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *peerkey = NULL; + struct wpabuf *secret = NULL; + size_t secret_len; + EC_POINT *pub; + EC_KEY *eckey = NULL; + + x = BN_bin2bn(key, inc_y ? len / 2 : len, NULL); + pub = EC_POINT_new(ecdh->ec->group); + if (!x || !pub) + goto fail; + + if (inc_y) { + y = BN_bin2bn(key + len / 2, len / 2, NULL); + if (!y) + goto fail; + if (!EC_POINT_set_affine_coordinates_GFp(ecdh->ec->group, pub, + x, y, + ecdh->ec->bnctx)) { + wpa_printf(MSG_ERROR, + "OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + } else if (!EC_POINT_set_compressed_coordinates_GFp(ecdh->ec->group, + pub, x, 0, + ecdh->ec->bnctx)) { + wpa_printf(MSG_ERROR, + "OpenSSL: EC_POINT_set_compressed_coordinates_GFp failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (!EC_POINT_is_on_curve(ecdh->ec->group, pub, ecdh->ec->bnctx)) { + wpa_printf(MSG_ERROR, + "OpenSSL: ECDH peer public key is not on curve"); + goto fail; + } + + eckey = EC_KEY_new_by_curve_name(ecdh->ec->nid); + if (!eckey || EC_KEY_set_public_key(eckey, pub) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EC_KEY_set_public_key failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + peerkey = EVP_PKEY_new(); + if (!peerkey || EVP_PKEY_set1_EC_KEY(peerkey, eckey) != 1) + goto fail; + + ctx = EVP_PKEY_CTX_new(ecdh->pkey, NULL); + if (!ctx || EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, peerkey) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EVP_PKEY_derive(1) failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + secret = wpabuf_alloc(secret_len); + if (!secret) + goto fail; + if (EVP_PKEY_derive(ctx, wpabuf_put(secret, secret_len), + &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EVP_PKEY_derive(2) failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + +done: + BN_free(x); + BN_free(y); + EC_KEY_free(eckey); + EC_POINT_free(pub); + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(peerkey); + return secret; +fail: + wpabuf_free(secret); + secret = NULL; + goto done; +} + + +void crypto_ecdh_deinit(struct crypto_ecdh *ecdh) +{ + if (ecdh) { + crypto_ec_deinit(ecdh->ec); + EVP_PKEY_free(ecdh->pkey); + os_free(ecdh); + } +} + #endif /* CONFIG_ECC */ diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c new file mode 100644 index 000000000000..b5a1e3fa31bc --- /dev/null +++ b/src/crypto/crypto_wolfssl.c @@ -0,0 +1,1791 @@ +/* + * Wrapper functions for libwolfssl + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" + +/* wolfSSL headers */ +#include <wolfssl/options.h> +#include <wolfssl/wolfcrypt/md4.h> +#include <wolfssl/wolfcrypt/md5.h> +#include <wolfssl/wolfcrypt/sha.h> +#include <wolfssl/wolfcrypt/sha256.h> +#include <wolfssl/wolfcrypt/sha512.h> +#include <wolfssl/wolfcrypt/hmac.h> +#include <wolfssl/wolfcrypt/pwdbased.h> +#include <wolfssl/wolfcrypt/arc4.h> +#include <wolfssl/wolfcrypt/des3.h> +#include <wolfssl/wolfcrypt/aes.h> +#include <wolfssl/wolfcrypt/dh.h> +#include <wolfssl/wolfcrypt/cmac.h> +#include <wolfssl/wolfcrypt/ecc.h> +#include <wolfssl/openssl/bn.h> + + +#ifndef CONFIG_FIPS + +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + Md4 md4; + size_t i; + + if (TEST_FAIL()) + return -1; + + wc_InitMd4(&md4); + + for (i = 0; i < num_elem; i++) + wc_Md4Update(&md4, addr[i], len[i]); + + wc_Md4Final(&md4, mac); + + return 0; +} + + +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + wc_Md5 md5; + size_t i; + + if (TEST_FAIL()) + return -1; + + wc_InitMd5(&md5); + + for (i = 0; i < num_elem; i++) + wc_Md5Update(&md5, addr[i], len[i]); + + wc_Md5Final(&md5, mac); + + return 0; +} + +#endif /* CONFIG_FIPS */ + + +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + wc_Sha sha; + size_t i; + + if (TEST_FAIL()) + return -1; + + wc_InitSha(&sha); + + for (i = 0; i < num_elem; i++) + wc_ShaUpdate(&sha, addr[i], len[i]); + + wc_ShaFinal(&sha, mac); + + return 0; +} + + +#ifndef NO_SHA256_WRAPPER +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + wc_Sha256 sha256; + size_t i; + + if (TEST_FAIL()) + return -1; + + wc_InitSha256(&sha256); + + for (i = 0; i < num_elem; i++) + wc_Sha256Update(&sha256, addr[i], len[i]); + + wc_Sha256Final(&sha256, mac); + + return 0; +} +#endif /* NO_SHA256_WRAPPER */ + + +#ifdef CONFIG_SHA384 +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + wc_Sha384 sha384; + size_t i; + + if (TEST_FAIL()) + return -1; + + wc_InitSha384(&sha384); + + for (i = 0; i < num_elem; i++) + wc_Sha384Update(&sha384, addr[i], len[i]); + + wc_Sha384Final(&sha384, mac); + + return 0; +} +#endif /* CONFIG_SHA384 */ + + +#ifdef CONFIG_SHA512 +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + wc_Sha512 sha512; + size_t i; + + if (TEST_FAIL()) + return -1; + + wc_InitSha512(&sha512); + + for (i = 0; i < num_elem; i++) + wc_Sha512Update(&sha512, addr[i], len[i]); + + wc_Sha512Final(&sha512, mac); + + return 0; +} +#endif /* CONFIG_SHA512 */ + + +static int wolfssl_hmac_vector(int type, const u8 *key, + size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac, + unsigned int mdlen) +{ + Hmac hmac; + size_t i; + + (void) mdlen; + + if (TEST_FAIL()) + return -1; + + if (wc_HmacSetKey(&hmac, type, key, (word32) key_len) != 0) + return -1; + for (i = 0; i < num_elem; i++) + if (wc_HmacUpdate(&hmac, addr[i], len[i]) != 0) + return -1; + if (wc_HmacFinal(&hmac, mac) != 0) + return -1; + return 0; +} + + +#ifndef CONFIG_FIPS + +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return wolfssl_hmac_vector(WC_MD5, key, key_len, num_elem, addr, len, + mac, 16); +} + + +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_FIPS */ + + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return wolfssl_hmac_vector(WC_SHA, key, key_len, num_elem, addr, len, + mac, 20); +} + + +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + + +#ifdef CONFIG_SHA256 + +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return wolfssl_hmac_vector(WC_SHA256, key, key_len, num_elem, addr, len, + mac, 32); +} + + +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA256 */ + + +#ifdef CONFIG_SHA384 + +int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return wolfssl_hmac_vector(WC_SHA384, key, key_len, num_elem, addr, len, + mac, 48); +} + + +int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA384 */ + + +#ifdef CONFIG_SHA512 + +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return wolfssl_hmac_vector(WC_SHA512, key, key_len, num_elem, addr, len, + mac, 64); +} + + +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA512 */ + + +int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen) +{ + if (wc_PBKDF2(buf, (const byte*)passphrase, os_strlen(passphrase), ssid, + ssid_len, iterations, buflen, WC_SHA) != 0) + return -1; + return 0; +} + + +#ifdef CONFIG_DES +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + Des des; + u8 pkey[8], next, tmp; + int i; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + wc_Des_SetKey(&des, pkey, NULL, DES_ENCRYPTION); + wc_Des_EcbEncrypt(&des, cypher, clear, DES_BLOCK_SIZE); + + return 0; +} +#endif /* CONFIG_DES */ + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + Aes *aes; + + if (TEST_FAIL()) + return NULL; + + aes = os_malloc(sizeof(Aes)); + if (!aes) + return NULL; + + if (wc_AesSetKey(aes, key, len, NULL, AES_ENCRYPTION) < 0) { + os_free(aes); + return NULL; + } + + return aes; +} + + +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + wc_AesEncryptDirect(ctx, crypt, plain); + return 0; +} + + +void aes_encrypt_deinit(void *ctx) +{ + os_free(ctx); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + Aes *aes; + + if (TEST_FAIL()) + return NULL; + + aes = os_malloc(sizeof(Aes)); + if (!aes) + return NULL; + + if (wc_AesSetKey(aes, key, len, NULL, AES_DECRYPTION) < 0) { + os_free(aes); + return NULL; + } + + return aes; +} + + +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + wc_AesDecryptDirect(ctx, plain, crypt); + return 0; +} + + +void aes_decrypt_deinit(void *ctx) +{ + os_free(ctx); +} + + +int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + Aes aes; + int ret; + + if (TEST_FAIL()) + return -1; + + ret = wc_AesSetKey(&aes, key, 16, iv, AES_ENCRYPTION); + if (ret != 0) + return -1; + + ret = wc_AesCbcEncrypt(&aes, data, data, data_len); + if (ret != 0) + return -1; + return 0; +} + + +int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + Aes aes; + int ret; + + if (TEST_FAIL()) + return -1; + + ret = wc_AesSetKey(&aes, key, 16, iv, AES_DECRYPTION); + if (ret != 0) + return -1; + + ret = wc_AesCbcDecrypt(&aes, data, data, data_len); + if (ret != 0) + return -1; + return 0; +} + + +int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher) +{ + int ret; + + if (TEST_FAIL()) + return -1; + + ret = wc_AesKeyWrap(kek, kek_len, plain, n * 8, cipher, (n + 1) * 8, + NULL); + return ret != (n + 1) * 8 ? -1 : 0; +} + + +int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, + u8 *plain) +{ + int ret; + + if (TEST_FAIL()) + return -1; + + ret = wc_AesKeyUnWrap(kek, kek_len, cipher, (n + 1) * 8, plain, n * 8, + NULL); + return ret != n * 8 ? -1 : 0; +} + + +#ifndef CONFIG_NO_RC4 +int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data, + size_t data_len) +{ +#ifndef NO_RC4 + Arc4 arc4; + unsigned char skip_buf[16]; + + wc_Arc4SetKey(&arc4, key, keylen); + + while (skip >= sizeof(skip_buf)) { + size_t len = skip; + + if (len > sizeof(skip_buf)) + len = sizeof(skip_buf); + wc_Arc4Process(&arc4, skip_buf, skip_buf, len); + skip -= len; + } + + wc_Arc4Process(&arc4, data, data, data_len); + + return 0; +#else /* NO_RC4 */ + return -1; +#endif /* NO_RC4 */ +} +#endif /* CONFIG_NO_RC4 */ + + +#if defined(EAP_IKEV2) || defined(EAP_IKEV2_DYNAMIC) \ + || defined(EAP_SERVER_IKEV2) +union wolfssl_cipher { + Aes aes; + Des3 des3; + Arc4 arc4; +}; + +struct crypto_cipher { + enum crypto_cipher_alg alg; + union wolfssl_cipher enc; + union wolfssl_cipher dec; +}; + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + switch (alg) { +#ifndef CONFIG_NO_RC4 +#ifndef NO_RC4 + case CRYPTO_CIPHER_ALG_RC4: + wc_Arc4SetKey(&ctx->enc.arc4, key, key_len); + wc_Arc4SetKey(&ctx->dec.arc4, key, key_len); + break; +#endif /* NO_RC4 */ +#endif /* CONFIG_NO_RC4 */ +#ifndef NO_AES + case CRYPTO_CIPHER_ALG_AES: + switch (key_len) { + case 16: + case 24: + case 32: + break; + default: + os_free(ctx); + return NULL; + } + if (wc_AesSetKey(&ctx->enc.aes, key, key_len, iv, + AES_ENCRYPTION) || + wc_AesSetKey(&ctx->dec.aes, key, key_len, iv, + AES_DECRYPTION)) { + os_free(ctx); + return NULL; + } + break; +#endif /* NO_AES */ +#ifndef NO_DES3 + case CRYPTO_CIPHER_ALG_3DES: + if (key_len != DES3_KEYLEN || + wc_Des3_SetKey(&ctx->enc.des3, key, iv, DES_ENCRYPTION) || + wc_Des3_SetKey(&ctx->dec.des3, key, iv, DES_DECRYPTION)) { + os_free(ctx); + return NULL; + } + break; +#endif /* NO_DES3 */ + case CRYPTO_CIPHER_ALG_RC2: + case CRYPTO_CIPHER_ALG_DES: + default: + os_free(ctx); + return NULL; + } + + ctx->alg = alg; + + return ctx; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + switch (ctx->alg) { +#ifndef CONFIG_NO_RC4 +#ifndef NO_RC4 + case CRYPTO_CIPHER_ALG_RC4: + wc_Arc4Process(&ctx->enc.arc4, crypt, plain, len); + return 0; +#endif /* NO_RC4 */ +#endif /* CONFIG_NO_RC4 */ +#ifndef NO_AES + case CRYPTO_CIPHER_ALG_AES: + if (wc_AesCbcEncrypt(&ctx->enc.aes, crypt, plain, len) != 0) + return -1; + return 0; +#endif /* NO_AES */ +#ifndef NO_DES3 + case CRYPTO_CIPHER_ALG_3DES: + if (wc_Des3_CbcEncrypt(&ctx->enc.des3, crypt, plain, len) != 0) + return -1; + return 0; +#endif /* NO_DES3 */ + default: + return -1; + } + return -1; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + switch (ctx->alg) { +#ifndef CONFIG_NO_RC4 +#ifndef NO_RC4 + case CRYPTO_CIPHER_ALG_RC4: + wc_Arc4Process(&ctx->dec.arc4, plain, crypt, len); + return 0; +#endif /* NO_RC4 */ +#endif /* CONFIG_NO_RC4 */ +#ifndef NO_AES + case CRYPTO_CIPHER_ALG_AES: + if (wc_AesCbcDecrypt(&ctx->dec.aes, plain, crypt, len) != 0) + return -1; + return 0; +#endif /* NO_AES */ +#ifndef NO_DES3 + case CRYPTO_CIPHER_ALG_3DES: + if (wc_Des3_CbcDecrypt(&ctx->dec.des3, plain, crypt, len) != 0) + return -1; + return 0; +#endif /* NO_DES3 */ + default: + return -1; + } + return -1; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + os_free(ctx); +} + +#endif + + +#ifdef CONFIG_WPS_NFC + +static const unsigned char RFC3526_PRIME_1536[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, + 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, + 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, + 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, + 0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static const unsigned char RFC3526_GENERATOR_1536[] = { + 0x02 +}; + +#define RFC3526_LEN sizeof(RFC3526_PRIME_1536) + + +void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) +{ + WC_RNG rng; + DhKey *ret = NULL; + DhKey *dh = NULL; + struct wpabuf *privkey = NULL; + struct wpabuf *pubkey = NULL; + word32 priv_sz, pub_sz; + + *priv = NULL; + wpabuf_free(*publ); + *publ = NULL; + + dh = XMALLOC(sizeof(DhKey), NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (!dh) + return NULL; + wc_InitDhKey(dh); + + if (wc_InitRng(&rng) != 0) { + XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return NULL; + } + + privkey = wpabuf_alloc(RFC3526_LEN); + pubkey = wpabuf_alloc(RFC3526_LEN); + if (!privkey || !pubkey) + goto done; + + if (wc_DhSetKey(dh, RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), + RFC3526_GENERATOR_1536, sizeof(RFC3526_GENERATOR_1536)) + != 0) + goto done; + + if (wc_DhGenerateKeyPair(dh, &rng, wpabuf_mhead(privkey), &priv_sz, + wpabuf_mhead(pubkey), &pub_sz) != 0) + goto done; + + wpabuf_put(privkey, priv_sz); + wpabuf_put(pubkey, pub_sz); + + ret = dh; + *priv = privkey; + *publ = pubkey; + dh = NULL; + privkey = NULL; + pubkey = NULL; +done: + wpabuf_clear_free(pubkey); + wpabuf_clear_free(privkey); + if (dh) { + wc_FreeDhKey(dh); + XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + wc_FreeRng(&rng); + return ret; +} + + +void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) +{ + DhKey *ret = NULL; + DhKey *dh; + byte *secret; + word32 secret_sz; + + dh = XMALLOC(sizeof(DhKey), NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (!dh) + return NULL; + wc_InitDhKey(dh); + + secret = XMALLOC(RFC3526_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (!secret) + goto done; + + if (wc_DhSetKey(dh, RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), + RFC3526_GENERATOR_1536, sizeof(RFC3526_GENERATOR_1536)) + != 0) + goto done; + + if (wc_DhAgree(dh, secret, &secret_sz, wpabuf_head(priv), + wpabuf_len(priv), RFC3526_GENERATOR_1536, + sizeof(RFC3526_GENERATOR_1536)) != 0) + goto done; + + if (secret_sz != wpabuf_len(publ) || + os_memcmp(secret, wpabuf_head(publ), secret_sz) != 0) + goto done; + + ret = dh; + dh = NULL; +done: + if (dh) { + wc_FreeDhKey(dh); + XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + XFREE(secret, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return ret; +} + + +struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, + const struct wpabuf *own_private) +{ + struct wpabuf *ret = NULL; + struct wpabuf *secret; + word32 secret_sz; + + secret = wpabuf_alloc(RFC3526_LEN); + if (!secret) + goto done; + + if (wc_DhAgree(ctx, wpabuf_mhead(secret), &secret_sz, + wpabuf_head(own_private), wpabuf_len(own_private), + wpabuf_head(peer_public), wpabuf_len(peer_public)) != 0) + goto done; + + wpabuf_put(secret, secret_sz); + + ret = secret; + secret = NULL; +done: + wpabuf_clear_free(secret); + return ret; +} + + +void dh5_free(void *ctx) +{ + if (!ctx) + return; + + wc_FreeDhKey(ctx); + XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); +} + +#endif /* CONFIG_WPS_NFC */ + + +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) +{ + int ret = -1; + WC_RNG rng; + DhKey *dh = NULL; + word32 priv_sz, pub_sz; + + if (TEST_FAIL()) + return -1; + + dh = os_malloc(sizeof(DhKey)); + if (!dh) + return -1; + wc_InitDhKey(dh); + + if (wc_InitRng(&rng) != 0) { + os_free(dh); + return -1; + } + + if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0) + goto done; + + if (wc_DhGenerateKeyPair(dh, &rng, privkey, &priv_sz, pubkey, &pub_sz) + != 0) + goto done; + + if (priv_sz < prime_len) { + size_t pad_sz = prime_len - priv_sz; + + os_memmove(privkey + pad_sz, privkey, priv_sz); + os_memset(privkey, 0, pad_sz); + } + + if (pub_sz < prime_len) { + size_t pad_sz = prime_len - pub_sz; + + os_memmove(pubkey + pad_sz, pubkey, pub_sz); + os_memset(pubkey, 0, pad_sz); + } + ret = 0; +done: + wc_FreeDhKey(dh); + os_free(dh); + wc_FreeRng(&rng); + return ret; +} + + +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) +{ + int ret = -1; + DhKey *dh; + word32 secret_sz; + + dh = os_malloc(sizeof(DhKey)); + if (!dh) + return -1; + wc_InitDhKey(dh); + + if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0) + goto done; + + if (wc_DhAgree(dh, secret, &secret_sz, privkey, privkey_len, pubkey, + pubkey_len) != 0) + goto done; + + *len = secret_sz; + ret = 0; +done: + wc_FreeDhKey(dh); + os_free(dh); + return ret; +} + + +#ifdef CONFIG_FIPS +int crypto_get_random(void *buf, size_t len) +{ + int ret = 0; + WC_RNG rng; + + if (wc_InitRng(&rng) != 0) + return -1; + if (wc_RNG_GenerateBlock(&rng, buf, len) != 0) + ret = -1; + wc_FreeRng(&rng); + return ret; +} +#endif /* CONFIG_FIPS */ + + +#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) +struct crypto_hash { + Hmac hmac; + int size; +}; + + +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct crypto_hash *ret = NULL; + struct crypto_hash *hash; + int type; + + hash = os_zalloc(sizeof(*hash)); + if (!hash) + goto done; + + switch (alg) { +#ifndef NO_MD5 + case CRYPTO_HASH_ALG_HMAC_MD5: + hash->size = 16; + type = WC_MD5; + break; +#endif /* NO_MD5 */ +#ifndef NO_SHA + case CRYPTO_HASH_ALG_HMAC_SHA1: + type = WC_SHA; + hash->size = 20; + break; +#endif /* NO_SHA */ +#ifdef CONFIG_SHA256 +#ifndef NO_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + type = WC_SHA256; + hash->size = 32; + break; +#endif /* NO_SHA256 */ +#endif /* CONFIG_SHA256 */ + default: + goto done; + } + + if (wc_HmacSetKey(&hash->hmac, type, key, key_len) != 0) + goto done; + + ret = hash; + hash = NULL; +done: + os_free(hash); + return ret; +} + + +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + if (!ctx) + return; + wc_HmacUpdate(&ctx->hmac, data, len); +} + + +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + int ret = 0; + + if (!ctx) + return -2; + + if (!mac || !len) + goto done; + + if (wc_HmacFinal(&ctx->hmac, mac) != 0) { + ret = -1; + goto done; + } + + *len = ctx->size; + ret = 0; +done: + bin_clear_free(ctx, sizeof(*ctx)); + return ret; +} + +#endif + + +int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + Cmac cmac; + size_t i; + word32 sz; + + if (TEST_FAIL()) + return -1; + + if (wc_InitCmac(&cmac, key, key_len, WC_CMAC_AES, NULL) != 0) + return -1; + + for (i = 0; i < num_elem; i++) + if (wc_CmacUpdate(&cmac, addr[i], len[i]) != 0) + return -1; + + sz = AES_BLOCK_SIZE; + if (wc_CmacFinal(&cmac, mac, &sz) != 0 || sz != AES_BLOCK_SIZE) + return -1; + + return 0; +} + + +int omac1_aes_128_vector(const u8 *key, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return omac1_aes_vector(key, 16, num_elem, addr, len, mac); +} + + +int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_128_vector(key, 1, &data, &data_len, mac); +} + + +int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_vector(key, 32, 1, &data, &data_len, mac); +} + + +struct crypto_bignum * crypto_bignum_init(void) +{ + mp_int *a; + + if (TEST_FAIL()) + return NULL; + + a = os_malloc(sizeof(*a)); + if (!a || mp_init(a) != MP_OKAY) { + os_free(a); + a = NULL; + } + + return (struct crypto_bignum *) a; +} + + +struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len) +{ + mp_int *a; + + if (TEST_FAIL()) + return NULL; + + a = (mp_int *) crypto_bignum_init(); + if (!a) + return NULL; + + if (mp_read_unsigned_bin(a, buf, len) != MP_OKAY) { + os_free(a); + a = NULL; + } + + return (struct crypto_bignum *) a; +} + + +void crypto_bignum_deinit(struct crypto_bignum *n, int clear) +{ + if (!n) + return; + + if (clear) + mp_forcezero((mp_int *) n); + mp_clear((mp_int *) n); + os_free((mp_int *) n); +} + + +int crypto_bignum_to_bin(const struct crypto_bignum *a, + u8 *buf, size_t buflen, size_t padlen) +{ + int num_bytes, offset; + + if (TEST_FAIL()) + return -1; + + if (padlen > buflen) + return -1; + + num_bytes = (mp_count_bits((mp_int *) a) + 7) / 8; + if ((size_t) num_bytes > buflen) + return -1; + if (padlen > (size_t) num_bytes) + offset = padlen - num_bytes; + else + offset = 0; + + os_memset(buf, 0, offset); + mp_to_unsigned_bin((mp_int *) a, buf + offset); + + return num_bytes + offset; +} + + +int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m) +{ + int ret = 0; + WC_RNG rng; + + if (wc_InitRng(&rng) != 0) + return -1; + if (mp_rand_prime((mp_int *) r, + (mp_count_bits((mp_int *) m) + 7) / 8 * 2, + &rng, NULL) != 0) + ret = -1; + if (ret == 0 && + mp_mod((mp_int *) r, (mp_int *) m, (mp_int *) r) != 0) + ret = -1; + wc_FreeRng(&rng); + return ret; +} + + +int crypto_bignum_add(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *r) +{ + return mp_add((mp_int *) a, (mp_int *) b, + (mp_int *) r) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_mod(const struct crypto_bignum *a, + const struct crypto_bignum *m, + struct crypto_bignum *r) +{ + return mp_mod((mp_int *) a, (mp_int *) m, + (mp_int *) r) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_exptmod(const struct crypto_bignum *b, + const struct crypto_bignum *e, + const struct crypto_bignum *m, + struct crypto_bignum *r) +{ + if (TEST_FAIL()) + return -1; + + return mp_exptmod((mp_int *) b, (mp_int *) e, (mp_int *) m, + (mp_int *) r) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_inverse(const struct crypto_bignum *a, + const struct crypto_bignum *m, + struct crypto_bignum *r) +{ + if (TEST_FAIL()) + return -1; + + return mp_invmod((mp_int *) a, (mp_int *) m, + (mp_int *) r) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_sub(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *r) +{ + if (TEST_FAIL()) + return -1; + + return mp_add((mp_int *) a, (mp_int *) b, + (mp_int *) r) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_div(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *d) +{ + if (TEST_FAIL()) + return -1; + + return mp_div((mp_int *) a, (mp_int *) b, (mp_int *) d, + NULL) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_mulmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *m, + struct crypto_bignum *d) +{ + if (TEST_FAIL()) + return -1; + + return mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) m, + (mp_int *) d) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_rshift(const struct crypto_bignum *a, int n, + struct crypto_bignum *r) +{ + if (mp_copy((mp_int *) a, (mp_int *) r) != MP_OKAY) + return -1; + mp_rshb((mp_int *) r, n); + return 0; +} + + +int crypto_bignum_cmp(const struct crypto_bignum *a, + const struct crypto_bignum *b) +{ + return mp_cmp((mp_int *) a, (mp_int *) b); +} + + +int crypto_bignum_bits(const struct crypto_bignum *a) +{ + return mp_count_bits((mp_int *) a); +} + + +int crypto_bignum_is_zero(const struct crypto_bignum *a) +{ + return mp_iszero((mp_int *) a); +} + + +int crypto_bignum_is_one(const struct crypto_bignum *a) +{ + return mp_isone((const mp_int *) a); +} + +int crypto_bignum_is_odd(const struct crypto_bignum *a) +{ + return mp_isodd((mp_int *) a); +} + + +int crypto_bignum_legendre(const struct crypto_bignum *a, + const struct crypto_bignum *p) +{ + mp_int t; + int ret; + int res = -2; + + if (TEST_FAIL()) + return -2; + + if (mp_init(&t) != MP_OKAY) + return -2; + + /* t = (p-1) / 2 */ + ret = mp_sub_d((mp_int *) p, 1, &t); + if (ret == MP_OKAY) + mp_rshb(&t, 1); + if (ret == MP_OKAY) + ret = mp_exptmod((mp_int *) a, &t, (mp_int *) p, &t); + if (ret == MP_OKAY) { + if (mp_isone(&t)) + res = 1; + else if (mp_iszero(&t)) + res = 0; + else + res = -1; + } + + mp_clear(&t); + return res; +} + + +#ifdef CONFIG_ECC + +int ecc_map(ecc_point *, mp_int *, mp_digit); +int ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, + mp_int *a, mp_int *modulus, mp_digit mp); + +struct crypto_ec { + ecc_key key; + mp_int a; + mp_int prime; + mp_int order; + mp_digit mont_b; + mp_int b; +}; + + +struct crypto_ec * crypto_ec_init(int group) +{ + int built = 0; + struct crypto_ec *e; + int curve_id; + + /* Map from IANA registry for IKE D-H groups to OpenSSL NID */ + switch (group) { + case 19: + curve_id = ECC_SECP256R1; + break; + case 20: + curve_id = ECC_SECP384R1; + break; + case 21: + curve_id = ECC_SECP521R1; + break; + case 25: + curve_id = ECC_SECP192R1; + break; + case 26: + curve_id = ECC_SECP224R1; + break; +#ifdef HAVE_ECC_BRAINPOOL + case 27: + curve_id = ECC_BRAINPOOLP224R1; + break; + case 28: + curve_id = ECC_BRAINPOOLP256R1; + break; + case 29: + curve_id = ECC_BRAINPOOLP384R1; + break; + case 30: + curve_id = ECC_BRAINPOOLP512R1; + break; +#endif /* HAVE_ECC_BRAINPOOL */ + default: + return NULL; + } + + e = os_zalloc(sizeof(*e)); + if (!e) + return NULL; + + if (wc_ecc_init(&e->key) != 0 || + wc_ecc_set_curve(&e->key, 0, curve_id) != 0 || + mp_init(&e->a) != MP_OKAY || + mp_init(&e->prime) != MP_OKAY || + mp_init(&e->order) != MP_OKAY || + mp_init(&e->b) != MP_OKAY || + mp_read_radix(&e->a, e->key.dp->Af, 16) != MP_OKAY || + mp_read_radix(&e->b, e->key.dp->Bf, 16) != MP_OKAY || + mp_read_radix(&e->prime, e->key.dp->prime, 16) != MP_OKAY || + mp_read_radix(&e->order, e->key.dp->order, 16) != MP_OKAY || + mp_montgomery_setup(&e->prime, &e->mont_b) != MP_OKAY) + goto done; + + built = 1; +done: + if (!built) { + crypto_ec_deinit(e); + e = NULL; + } + return e; +} + + +void crypto_ec_deinit(struct crypto_ec* e) +{ + if (!e) + return; + + mp_clear(&e->b); + mp_clear(&e->order); + mp_clear(&e->prime); + mp_clear(&e->a); + wc_ecc_free(&e->key); + os_free(e); +} + + +int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor) +{ + if (!e || !cofactor) + return -1; + + mp_set((mp_int *) cofactor, e->key.dp->cofactor); + return 0; +} + + +struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) +{ + if (TEST_FAIL()) + return NULL; + if (!e) + return NULL; + return (struct crypto_ec_point *) wc_ecc_new_point(); +} + + +size_t crypto_ec_prime_len(struct crypto_ec *e) +{ + return (mp_count_bits(&e->prime) + 7) / 8; +} + + +size_t crypto_ec_prime_len_bits(struct crypto_ec *e) +{ + return mp_count_bits(&e->prime); +} + + +size_t crypto_ec_order_len(struct crypto_ec *e) +{ + return (mp_count_bits(&e->order) + 7) / 8; +} + + +const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e) +{ + return (const struct crypto_bignum *) &e->prime; +} + + +const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e) +{ + return (const struct crypto_bignum *) &e->order; +} + + +void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear) +{ + ecc_point *point = (ecc_point *) p; + + if (!p) + return; + + if (clear) { + mp_forcezero(point->x); + mp_forcezero(point->y); + mp_forcezero(point->z); + } + wc_ecc_del_point(point); +} + + +int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p, + struct crypto_bignum *x) +{ + return mp_copy(((ecc_point *) p)->x, (mp_int *) x) == MP_OKAY ? 0 : -1; +} + + +int crypto_ec_point_to_bin(struct crypto_ec *e, + const struct crypto_ec_point *point, u8 *x, u8 *y) +{ + ecc_point *p = (ecc_point *) point; + + if (TEST_FAIL()) + return -1; + + if (!mp_isone(p->z)) { + if (ecc_map(p, &e->prime, e->mont_b) != MP_OKAY) + return -1; + } + + if (x) { + if (crypto_bignum_to_bin((struct crypto_bignum *)p->x, x, + e->key.dp->size, + e->key.dp->size) <= 0) + return -1; + } + + if (y) { + if (crypto_bignum_to_bin((struct crypto_bignum *) p->y, y, + e->key.dp->size, + e->key.dp->size) <= 0) + return -1; + } + + return 0; +} + + +struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, + const u8 *val) +{ + ecc_point *point = NULL; + int loaded = 0; + + if (TEST_FAIL()) + return NULL; + + point = wc_ecc_new_point(); + if (!point) + goto done; + + if (mp_read_unsigned_bin(point->x, val, e->key.dp->size) != MP_OKAY) + goto done; + val += e->key.dp->size; + if (mp_read_unsigned_bin(point->y, val, e->key.dp->size) != MP_OKAY) + goto done; + mp_set(point->z, 1); + + loaded = 1; +done: + if (!loaded) { + wc_ecc_del_point(point); + point = NULL; + } + return (struct crypto_ec_point *) point; +} + + +int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, + const struct crypto_ec_point *b, + struct crypto_ec_point *c) +{ + mp_int mu; + ecc_point *ta = NULL, *tb = NULL; + ecc_point *pa = (ecc_point *) a, *pb = (ecc_point *) b; + mp_int *modulus = &e->prime; + int ret; + + if (TEST_FAIL()) + return -1; + + ret = mp_init(&mu); + if (ret != MP_OKAY) + return -1; + + ret = mp_montgomery_calc_normalization(&mu, modulus); + if (ret != MP_OKAY) { + mp_clear(&mu); + return -1; + } + + if (!mp_isone(&mu)) { + ta = wc_ecc_new_point(); + if (!ta) { + mp_clear(&mu); + return -1; + } + tb = wc_ecc_new_point(); + if (!tb) { + wc_ecc_del_point(ta); + mp_clear(&mu); + return -1; + } + + if (mp_mulmod(pa->x, &mu, modulus, ta->x) != MP_OKAY || + mp_mulmod(pa->y, &mu, modulus, ta->y) != MP_OKAY || + mp_mulmod(pa->z, &mu, modulus, ta->z) != MP_OKAY || + mp_mulmod(pb->x, &mu, modulus, tb->x) != MP_OKAY || + mp_mulmod(pb->y, &mu, modulus, tb->y) != MP_OKAY || + mp_mulmod(pb->z, &mu, modulus, tb->z) != MP_OKAY) { + ret = -1; + goto end; + } + pa = ta; + pb = tb; + } + + ret = ecc_projective_add_point(pa, pb, (ecc_point *) c, &e->a, + &e->prime, e->mont_b); + if (ret != 0) { + ret = -1; + goto end; + } + + if (ecc_map((ecc_point *) c, &e->prime, e->mont_b) != MP_OKAY) + ret = -1; + else + ret = 0; +end: + wc_ecc_del_point(tb); + wc_ecc_del_point(ta); + mp_clear(&mu); + return ret; +} + + +int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, + const struct crypto_bignum *b, + struct crypto_ec_point *res) +{ + int ret; + + if (TEST_FAIL()) + return -1; + + ret = wc_ecc_mulmod((mp_int *) b, (ecc_point *) p, (ecc_point *) res, + &e->a, &e->prime, 1); + return ret == 0 ? 0 : -1; +} + + +int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p) +{ + ecc_point *point = (ecc_point *) p; + + if (TEST_FAIL()) + return -1; + + if (mp_sub(&e->prime, point->y, point->y) != MP_OKAY) + return -1; + + return 0; +} + + +int crypto_ec_point_solve_y_coord(struct crypto_ec *e, + struct crypto_ec_point *p, + const struct crypto_bignum *x, int y_bit) +{ + byte buf[1 + 2 * MAX_ECC_BYTES]; + int ret; + int prime_len = crypto_ec_prime_len(e); + + if (TEST_FAIL()) + return -1; + + buf[0] = y_bit ? ECC_POINT_COMP_ODD : ECC_POINT_COMP_EVEN; + ret = crypto_bignum_to_bin(x, buf + 1, prime_len, prime_len); + if (ret <= 0) + return -1; + ret = wc_ecc_import_point_der(buf, 1 + 2 * ret, e->key.idx, + (ecc_point *) p); + if (ret != 0) + return -1; + + return 0; +} + + +struct crypto_bignum * +crypto_ec_point_compute_y_sqr(struct crypto_ec *e, + const struct crypto_bignum *x) +{ + mp_int *y2 = NULL; + mp_int t; + int calced = 0; + + if (TEST_FAIL()) + return NULL; + + if (mp_init(&t) != MP_OKAY) + return NULL; + + y2 = (mp_int *) crypto_bignum_init(); + if (!y2) + goto done; + + if (mp_sqrmod((mp_int *) x, &e->prime, y2) != 0 || + mp_mulmod((mp_int *) x, y2, &e->prime, y2) != 0 || + mp_mulmod((mp_int *) x, &e->a, &e->prime, &t) != 0 || + mp_addmod(y2, &t, &e->prime, y2) != 0 || + mp_addmod(y2, &e->b, &e->prime, y2) != 0) + goto done; + + calced = 1; +done: + if (!calced) { + if (y2) { + mp_clear(y2); + os_free(y2); + } + mp_clear(&t); + } + + return (struct crypto_bignum *) y2; +} + + +int crypto_ec_point_is_at_infinity(struct crypto_ec *e, + const struct crypto_ec_point *p) +{ + return wc_ecc_point_is_at_infinity((ecc_point *) p); +} + + +int crypto_ec_point_is_on_curve(struct crypto_ec *e, + const struct crypto_ec_point *p) +{ + return wc_ecc_is_point((ecc_point *) p, &e->a, &e->b, &e->prime) == + MP_OKAY; +} + + +int crypto_ec_point_cmp(const struct crypto_ec *e, + const struct crypto_ec_point *a, + const struct crypto_ec_point *b) +{ + return wc_ecc_cmp_point((ecc_point *) a, (ecc_point *) b); +} + + +struct crypto_ecdh { + struct crypto_ec *ec; +}; + +struct crypto_ecdh * crypto_ecdh_init(int group) +{ + struct crypto_ecdh *ecdh = NULL; + WC_RNG rng; + int ret; + + if (wc_InitRng(&rng) != 0) + goto fail; + + ecdh = os_zalloc(sizeof(*ecdh)); + if (!ecdh) + goto fail; + + ecdh->ec = crypto_ec_init(group); + if (!ecdh->ec) + goto fail; + + ret = wc_ecc_make_key_ex(&rng, ecdh->ec->key.dp->size, &ecdh->ec->key, + ecdh->ec->key.dp->id); + if (ret < 0) + goto fail; + +done: + wc_FreeRng(&rng); + + return ecdh; +fail: + crypto_ecdh_deinit(ecdh); + ecdh = NULL; + goto done; +} + + +void crypto_ecdh_deinit(struct crypto_ecdh *ecdh) +{ + if (ecdh) { + crypto_ec_deinit(ecdh->ec); + os_free(ecdh); + } +} + + +struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y) +{ + struct wpabuf *buf = NULL; + int ret; + int len = ecdh->ec->key.dp->size; + + buf = wpabuf_alloc(inc_y ? 2 * len : len); + if (!buf) + goto fail; + + ret = crypto_bignum_to_bin((struct crypto_bignum *) + ecdh->ec->key.pubkey.x, wpabuf_put(buf, len), + len, len); + if (ret < 0) + goto fail; + if (inc_y) { + ret = crypto_bignum_to_bin((struct crypto_bignum *) + ecdh->ec->key.pubkey.y, + wpabuf_put(buf, len), len, len); + if (ret < 0) + goto fail; + } + +done: + return buf; +fail: + wpabuf_free(buf); + buf = NULL; + goto done; +} + + +struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y, + const u8 *key, size_t len) +{ + int ret; + struct wpabuf *pubkey = NULL; + struct wpabuf *secret = NULL; + word32 key_len = ecdh->ec->key.dp->size; + ecc_point *point = NULL; + size_t need_key_len = inc_y ? 2 * key_len : key_len; + + if (len < need_key_len) + goto fail; + pubkey = wpabuf_alloc(1 + 2 * key_len); + if (!pubkey) + goto fail; + wpabuf_put_u8(pubkey, inc_y ? ECC_POINT_UNCOMP : ECC_POINT_COMP_EVEN); + wpabuf_put_data(pubkey, key, need_key_len); + + point = wc_ecc_new_point(); + if (!point) + goto fail; + + ret = wc_ecc_import_point_der(wpabuf_mhead(pubkey), 1 + 2 * key_len, + ecdh->ec->key.idx, point); + if (ret != MP_OKAY) + goto fail; + + secret = wpabuf_alloc(key_len); + if (!secret) + goto fail; + + ret = wc_ecc_shared_secret_ex(&ecdh->ec->key, point, + wpabuf_put(secret, key_len), &key_len); + if (ret != MP_OKAY) + goto fail; + +done: + wc_ecc_del_point(point); + wpabuf_free(pubkey); + return secret; +fail: + wpabuf_free(secret); + secret = NULL; + goto done; +} + +#endif /* CONFIG_ECC */ diff --git a/src/crypto/des-internal.c b/src/crypto/des-internal.c index dec39ef8c61d..4ed6957802b0 100644 --- a/src/crypto/des-internal.c +++ b/src/crypto/des-internal.c @@ -48,7 +48,7 @@ static const u32 bytebit[8] = { - 0200, 0100, 040, 020, 010, 04, 02, 01 + 0200, 0100, 040, 020, 010, 04, 02, 01 }; static const u32 bigbyte[24] = @@ -58,22 +58,22 @@ static const u32 bigbyte[24] = 0x8000UL, 0x4000UL, 0x2000UL, 0x1000UL, 0x800UL, 0x400UL, 0x200UL, 0x100UL, 0x80UL, 0x40UL, 0x20UL, 0x10UL, - 0x8UL, 0x4UL, 0x2UL, 0x1L + 0x8UL, 0x4UL, 0x2UL, 0x1L }; /* Use the key schedule specific in the standard (ANSI X3.92-1981) */ static const u8 pc1[56] = { - 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, - 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, + 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, + 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, - 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 + 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 }; static const u8 totrot[16] = { 1, 2, 4, 6, - 8, 10, 12, 14, - 15, 17, 19, 21, + 8, 10, 12, 14, + 15, 17, 19, 21, 23, 25, 27, 28 }; @@ -396,7 +396,7 @@ static void desfunc(u32 *block, const u32 *keys) /* wpa_supplicant/hostapd specific wrapper */ -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { u8 pkey[8], next, tmp; int i; @@ -421,6 +421,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) os_memset(pkey, 0, sizeof(pkey)); os_memset(ek, 0, sizeof(ek)); + return 0; } diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c index 7912361ff8c6..a9b770ec1f16 100644 --- a/src/crypto/dh_groups.c +++ b/src/crypto/dh_groups.c @@ -1151,7 +1151,7 @@ static const u8 dh_group24_order[] = { { id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \ dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \ dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe } - + static const struct dh_group dh_groups[] = { DH_GROUP(5, 1), @@ -1203,19 +1203,6 @@ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv) if (*priv == NULL) return NULL; - if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len)) - { - wpabuf_clear_free(*priv); - *priv = NULL; - return NULL; - } - - if (os_memcmp(wpabuf_head(*priv), dh->prime, dh->prime_len) > 0) { - /* Make sure private value is smaller than prime */ - *(wpabuf_mhead_u8(*priv)) = 0; - } - wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv); - pv_len = dh->prime_len; pv = wpabuf_alloc(pv_len); if (pv == NULL) { @@ -1223,17 +1210,17 @@ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv) *priv = NULL; return NULL; } - if (crypto_mod_exp(dh->generator, dh->generator_len, - wpabuf_head(*priv), wpabuf_len(*priv), - dh->prime, dh->prime_len, wpabuf_mhead(pv), - &pv_len) < 0) { + if (crypto_dh_init(*dh->generator, dh->prime, dh->prime_len, + wpabuf_mhead(*priv), wpabuf_mhead(pv)) < 0) { wpabuf_clear_free(pv); - wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); + wpa_printf(MSG_INFO, "DH: crypto_dh_init failed"); wpabuf_clear_free(*priv); *priv = NULL; return NULL; } - wpabuf_put(pv, pv_len); + wpabuf_put(*priv, dh->prime_len); + wpabuf_put(pv, dh->prime_len); + wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv); wpa_hexdump_buf(MSG_DEBUG, "DH: public value", pv); return pv; @@ -1261,12 +1248,14 @@ struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public, shared = wpabuf_alloc(shared_len); if (shared == NULL) return NULL; - if (crypto_mod_exp(wpabuf_head(peer_public), wpabuf_len(peer_public), - wpabuf_head(own_private), wpabuf_len(own_private), - dh->prime, dh->prime_len, - wpabuf_mhead(shared), &shared_len) < 0) { + if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len, + wpabuf_head(own_private), + wpabuf_len(own_private), + wpabuf_head(peer_public), + wpabuf_len(peer_public), + wpabuf_mhead(shared), &shared_len) < 0) { wpabuf_clear_free(shared); - wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); + wpa_printf(MSG_INFO, "DH: crypto_dh_derive_secret failed"); return NULL; } wpabuf_put(shared, shared_len); diff --git a/src/crypto/fips_prf_wolfssl.c b/src/crypto/fips_prf_wolfssl.c new file mode 100644 index 000000000000..feb39db5a6d9 --- /dev/null +++ b/src/crypto/fips_prf_wolfssl.c @@ -0,0 +1,87 @@ +/* + * FIPS 186-2 PRF for libcrypto + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include <wolfssl/options.h> +#include <wolfssl/wolfcrypt/sha.h> + +#include "common.h" +#include "crypto.h" + + +static void sha1_transform(u32 *state, const u8 data[64]) +{ + wc_Sha sha; + + os_memset(&sha, 0, sizeof(sha)); + sha.digest[0] = state[0]; + sha.digest[1] = state[1]; + sha.digest[2] = state[2]; + sha.digest[3] = state[3]; + sha.digest[4] = state[4]; + wc_ShaUpdate(&sha, data, 64); + state[0] = sha.digest[0]; + state[1] = sha.digest[1]; + state[2] = sha.digest[2]; + state[3] = sha.digest[3]; + state[4] = sha.digest[4]; +} + + +int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) +{ + u8 xkey[64]; + u32 t[5], _t[5]; + int i, j, m, k; + u8 *xpos = x; + u32 carry; + + if (seed_len < sizeof(xkey)) + os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len); + else + seed_len = sizeof(xkey); + + /* FIPS 186-2 + change notice 1 */ + + os_memcpy(xkey, seed, seed_len); + t[0] = 0x67452301; + t[1] = 0xEFCDAB89; + t[2] = 0x98BADCFE; + t[3] = 0x10325476; + t[4] = 0xC3D2E1F0; + + m = xlen / 40; + for (j = 0; j < m; j++) { + /* XSEED_j = 0 */ + for (i = 0; i < 2; i++) { + /* XVAL = (XKEY + XSEED_j) mod 2^b */ + + /* w_i = G(t, XVAL) */ + os_memcpy(_t, t, 20); + sha1_transform(_t, xkey); + WPA_PUT_BE32(xpos, _t[0]); + WPA_PUT_BE32(xpos + 4, _t[1]); + WPA_PUT_BE32(xpos + 8, _t[2]); + WPA_PUT_BE32(xpos + 12, _t[3]); + WPA_PUT_BE32(xpos + 16, _t[4]); + + /* XKEY = (1 + XKEY + w_i) mod 2^b */ + carry = 1; + for (k = 19; k >= 0; k--) { + carry += xkey[k] + xpos[k]; + xkey[k] = carry & 0xff; + carry >>= 8; + } + + xpos += 20; + } + /* x_j = w_0|w_1 */ + } + + return 0; +} diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c index d0d6a96af2bc..aff7d33f4ea4 100644 --- a/src/crypto/ms_funcs.c +++ b/src/crypto/ms_funcs.c @@ -140,17 +140,20 @@ int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash) * @challenge: 8-octet Challenge (IN) * @password_hash: 16-octet PasswordHash (IN) * @response: 24-octet Response (OUT) + * Returns: 0 on success, -1 on failure */ -void challenge_response(const u8 *challenge, const u8 *password_hash, - u8 *response) +int challenge_response(const u8 *challenge, const u8 *password_hash, + u8 *response) { u8 zpwd[7]; - des_encrypt(challenge, password_hash, response); - des_encrypt(challenge, password_hash + 7, response + 8); + + if (des_encrypt(challenge, password_hash, response) < 0 || + des_encrypt(challenge, password_hash + 7, response + 8) < 0) + return -1; zpwd[0] = password_hash[14]; zpwd[1] = password_hash[15]; os_memset(zpwd + 2, 0, 5); - des_encrypt(challenge, zpwd, response + 16); + return des_encrypt(challenge, zpwd, response + 16); } @@ -175,9 +178,9 @@ int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, if (challenge_hash(peer_challenge, auth_challenge, username, username_len, challenge) || - nt_password_hash(password, password_len, password_hash)) + nt_password_hash(password, password_len, password_hash) || + challenge_response(challenge, password_hash, response)) return -1; - challenge_response(challenge, password_hash, response); return 0; } @@ -202,9 +205,9 @@ int generate_nt_response_pwhash(const u8 *auth_challenge, if (challenge_hash(peer_challenge, auth_challenge, username, username_len, - challenge)) + challenge) || + challenge_response(challenge, password_hash, response)) return -1; - challenge_response(challenge, password_hash, response); return 0; } @@ -304,9 +307,10 @@ int nt_challenge_response(const u8 *challenge, const u8 *password, size_t password_len, u8 *response) { u8 password_hash[16]; - if (nt_password_hash(password, password_len, password_hash)) + + if (nt_password_hash(password, password_len, password_hash) || + challenge_response(challenge, password_hash, response)) return -1; - challenge_response(challenge, password_hash, response); return 0; } @@ -487,12 +491,15 @@ int new_password_encrypted_with_old_nt_password_hash( * @password_hash: 16-octer PasswordHash (IN) * @block: 16-octet Block (IN) * @cypher: 16-octer Cypher (OUT) + * Returns: 0 on success, -1 on failure */ -void nt_password_hash_encrypted_with_block(const u8 *password_hash, - const u8 *block, u8 *cypher) +int nt_password_hash_encrypted_with_block(const u8 *password_hash, + const u8 *block, u8 *cypher) { - des_encrypt(password_hash, block, cypher); - des_encrypt(password_hash + 8, block + 7, cypher + 8); + if (des_encrypt(password_hash, block, cypher) < 0 || + des_encrypt(password_hash + 8, block + 7, cypher + 8) < 0) + return -1; + return 0; } @@ -515,10 +522,10 @@ int old_nt_password_hash_encrypted_with_new_nt_password_hash( if (nt_password_hash(old_password, old_password_len, old_password_hash) || nt_password_hash(new_password, new_password_len, - new_password_hash)) + new_password_hash) || + nt_password_hash_encrypted_with_block(old_password_hash, + new_password_hash, + encrypted_password_hash)) return -1; - nt_password_hash_encrypted_with_block(old_password_hash, - new_password_hash, - encrypted_password_hash); return 0; } diff --git a/src/crypto/ms_funcs.h b/src/crypto/ms_funcs.h index b5b5918e1a55..b8d55f05326c 100644 --- a/src/crypto/ms_funcs.h +++ b/src/crypto/ms_funcs.h @@ -31,8 +31,8 @@ int generate_authenticator_response_pwhash( int nt_challenge_response(const u8 *challenge, const u8 *password, size_t password_len, u8 *response); -void challenge_response(const u8 *challenge, const u8 *password_hash, - u8 *response); +int challenge_response(const u8 *challenge, const u8 *password_hash, + u8 *response); int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, const u8 *username, size_t username_len, u8 *challenge); int nt_password_hash(const u8 *password, size_t password_len, @@ -50,8 +50,8 @@ int __must_check new_password_encrypted_with_old_nt_password_hash( const u8 *new_password, size_t new_password_len, const u8 *old_password, size_t old_password_len, u8 *encrypted_pw_block); -void nt_password_hash_encrypted_with_block(const u8 *password_hash, - const u8 *block, u8 *cypher); +int nt_password_hash_encrypted_with_block(const u8 *password_hash, + const u8 *block, u8 *cypher); int old_nt_password_hash_encrypted_with_new_nt_password_hash( const u8 *new_password, size_t new_password_len, const u8 *old_password, size_t old_password_len, diff --git a/src/crypto/random.c b/src/crypto/random.c index 3a86a93a46a8..c278d9cb9de9 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -54,7 +54,6 @@ static int random_fd = -1; static unsigned int own_pool_ready = 0; #define RANDOM_ENTROPY_SIZE 20 static char *random_entropy_file = NULL; -static int random_entropy_file_read = 0; #define MIN_COLLECT_ENTROPY 1000 static unsigned int entropy = 0; @@ -66,6 +65,9 @@ static void random_write_entropy(void); static u32 __ROL32(u32 x, u32 y) { + if (y == 0) + return x; + return (x << (y & 31)) | (x >> (32 - (y & 31))); } @@ -354,7 +356,6 @@ static void random_read_entropy(void) own_pool_ready = (u8) buf[0]; random_add_randomness(buf + 1, RANDOM_ENTROPY_SIZE); - random_entropy_file_read = 1; os_free(buf); wpa_printf(MSG_DEBUG, "random: Added entropy from %s " "(own_pool_ready=%u)", diff --git a/src/crypto/sha1-internal.c b/src/crypto/sha1-internal.c index ffcba66af652..a4917070f196 100644 --- a/src/crypto/sha1-internal.c +++ b/src/crypto/sha1-internal.c @@ -53,7 +53,7 @@ By Steve Reid <sreid@sea-to-sky.net> 100% Public Domain ----------------- -Modified 7/98 +Modified 7/98 By James H. Brown <jbrown@burgoyne.com> Still 100% Public Domain @@ -75,7 +75,7 @@ Since the file IO in main() reads 16K at a time, any file 8K or larger would be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million "a"s). -I also changed the declaration of variables i & j in SHA1Update to +I also changed the declaration of variables i & j in SHA1Update to unsigned long from unsigned int for the same reason. These changes should make no difference to any 32 bit implementations since @@ -102,7 +102,7 @@ Still 100% public domain Modified 4/01 By Saul Kravitz <Saul.Kravitz@celera.com> Still 100% PD -Modified to run on Compaq Alpha hardware. +Modified to run on Compaq Alpha hardware. ----------------- Modified 4/01 @@ -162,7 +162,7 @@ void SHAPrintContext(SHA1_CTX *context, char *msg) { printf("%s (%d,%d) %x %x %x %x %x\n", msg, - context->count[0], context->count[1], + context->count[0], context->count[1], context->state[0], context->state[1], context->state[2], diff --git a/src/crypto/sha256-internal.c b/src/crypto/sha256-internal.c index 86a548ee472d..ff1e2ba1686d 100644 --- a/src/crypto/sha256-internal.c +++ b/src/crypto/sha256-internal.c @@ -69,7 +69,7 @@ static const unsigned long K[64] = { ( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \ ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL) #define Ch(x,y,z) (z ^ (x & (y ^ z))) -#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) #define S(x, n) RORc((x), (n)) #define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) #define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) @@ -100,7 +100,7 @@ static int sha256_compress(struct sha256_state *md, unsigned char *buf) for (i = 16; i < 64; i++) { W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; - } + } /* Compress */ #define RND(a,b,c,d,e,f,g,h,i) \ @@ -111,7 +111,7 @@ static int sha256_compress(struct sha256_state *md, unsigned char *buf) for (i = 0; i < 64; ++i) { RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i); - t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; + t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; } diff --git a/src/crypto/sha256-kdf.c b/src/crypto/sha256-kdf.c index e7509ce41aba..af7d954d8a44 100644 --- a/src/crypto/sha256-kdf.c +++ b/src/crypto/sha256-kdf.c @@ -1,6 +1,6 @@ /* - * HMAC-SHA256 KDF (RFC 5295) - * Copyright (c) 2014, Jouni Malinen <j@w1.fi> + * HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869) + * Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -16,7 +16,8 @@ * hmac_sha256_kdf - HMAC-SHA256 based KDF (RFC 5295) * @secret: Key for KDF * @secret_len: Length of the key in bytes - * @label: A unique label for each purpose of the KDF + * @label: A unique label for each purpose of the KDF or %NULL to select + * RFC 5869 HKDF-Expand() with arbitrary seed (= info) * @seed: Seed value to bind into the key * @seed_len: Length of the seed * @out: Buffer for the generated pseudo-random key @@ -24,7 +25,9 @@ * Returns: 0 on success, -1 on failure. * * This function is used to derive new, cryptographically separate keys from a - * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. + * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used + * with label = NULL and seed = info, this matches HKDF-Expand() defined in + * RFC 5869, Chapter 2.3. */ int hmac_sha256_kdf(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, @@ -38,8 +41,13 @@ int hmac_sha256_kdf(const u8 *secret, size_t secret_len, addr[0] = T; len[0] = SHA256_MAC_LEN; - addr[1] = (const unsigned char *) label; - len[1] = os_strlen(label) + 1; + if (label) { + addr[1] = (const unsigned char *) label; + len[1] = os_strlen(label) + 1; + } else { + addr[1] = (const u8 *) ""; + len[1] = 0; + } addr[2] = seed; len[2] = seed_len; addr[3] = &iter; diff --git a/src/crypto/sha384-kdf.c b/src/crypto/sha384-kdf.c new file mode 100644 index 000000000000..1d196279086e --- /dev/null +++ b/src/crypto/sha384-kdf.c @@ -0,0 +1,87 @@ +/* + * HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869) + * Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha384.h" + + +/** + * hmac_sha384_kdf - HMAC-SHA384 based KDF (RFC 5295) + * @secret: Key for KDF + * @secret_len: Length of the key in bytes + * @label: A unique label for each purpose of the KDF or %NULL to select + * RFC 5869 HKDF-Expand() with arbitrary seed (= info) + * @seed: Seed value to bind into the key + * @seed_len: Length of the seed + * @out: Buffer for the generated pseudo-random key + * @outlen: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure. + * + * This function is used to derive new, cryptographically separate keys from a + * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used + * with label = NULL and seed = info, this matches HKDF-Expand() defined in + * RFC 5869, Chapter 2.3. + */ +int hmac_sha384_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen) +{ + u8 T[SHA384_MAC_LEN]; + u8 iter = 1; + const unsigned char *addr[4]; + size_t len[4]; + size_t pos, clen; + + addr[0] = T; + len[0] = SHA384_MAC_LEN; + if (label) { + addr[1] = (const unsigned char *) label; + len[1] = os_strlen(label) + 1; + } else { + addr[1] = (const u8 *) ""; + len[1] = 0; + } + addr[2] = seed; + len[2] = seed_len; + addr[3] = &iter; + len[3] = 1; + + if (hmac_sha384_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0) + return -1; + + pos = 0; + for (;;) { + clen = outlen - pos; + if (clen > SHA384_MAC_LEN) + clen = SHA384_MAC_LEN; + os_memcpy(out + pos, T, clen); + pos += clen; + + if (pos == outlen) + break; + + if (iter == 255) { + os_memset(out, 0, outlen); + os_memset(T, 0, SHA384_MAC_LEN); + return -1; + } + iter++; + + if (hmac_sha384_vector(secret, secret_len, 4, addr, len, T) < 0) + { + os_memset(out, 0, outlen); + os_memset(T, 0, SHA384_MAC_LEN); + return -1; + } + } + + os_memset(T, 0, SHA384_MAC_LEN); + return 0; +} diff --git a/src/crypto/sha384-prf.c b/src/crypto/sha384-prf.c index 653920ba283d..03e3cb353a3d 100644 --- a/src/crypto/sha384-prf.c +++ b/src/crypto/sha384-prf.c @@ -1,6 +1,6 @@ /* * SHA384-based KDF (IEEE 802.11ac) - * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -22,14 +22,16 @@ * @data_len: Length of the data * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure * * This function is used to derive new, cryptographically separate keys from a * given key. */ -void sha384_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +int sha384_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) { - sha384_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8); + return sha384_prf_bits(key, key_len, label, data, data_len, buf, + buf_len * 8); } @@ -42,15 +44,16 @@ void sha384_prf(const u8 *key, size_t key_len, const char *label, * @data_len: Length of the data * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bits of key to generate + * Returns: 0 on success, -1 on failure * * This function is used to derive new, cryptographically separate keys from a * given key. If the requested buf_len is not divisible by eight, the least * significant 1-7 bits of the last octet in the output are not part of the * requested output. */ -void sha384_prf_bits(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, - size_t buf_len_bits) +int sha384_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits) { u16 counter = 1; size_t pos, plen; @@ -75,11 +78,14 @@ void sha384_prf_bits(const u8 *key, size_t key_len, const char *label, plen = buf_len - pos; WPA_PUT_LE16(counter_le, counter); if (plen >= SHA384_MAC_LEN) { - hmac_sha384_vector(key, key_len, 4, addr, len, - &buf[pos]); + if (hmac_sha384_vector(key, key_len, 4, addr, len, + &buf[pos]) < 0) + return -1; pos += SHA384_MAC_LEN; } else { - hmac_sha384_vector(key, key_len, 4, addr, len, hash); + if (hmac_sha384_vector(key, key_len, 4, addr, len, + hash) < 0) + return -1; os_memcpy(&buf[pos], hash, plen); pos += plen; break; @@ -97,4 +103,6 @@ void sha384_prf_bits(const u8 *key, size_t key_len, const char *label, } os_memset(hash, 0, sizeof(hash)); + + return 0; } diff --git a/src/crypto/sha384.c b/src/crypto/sha384.c new file mode 100644 index 000000000000..ee136ce99b7e --- /dev/null +++ b/src/crypto/sha384.c @@ -0,0 +1,104 @@ +/* + * SHA-384 hash implementation and interface functions + * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha384.h" +#include "crypto.h" + + +/** + * hmac_sha384_vector - HMAC-SHA384 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (48 bytes) + * Returns: 0 on success, -1 on failure + */ +int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + unsigned char k_pad[128]; /* padding - key XORd with ipad/opad */ + unsigned char tk[48]; + const u8 *_addr[6]; + size_t _len[6], i; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return -1; + } + + /* if key is longer than 128 bytes reset it to key = SHA384(key) */ + if (key_len > 128) { + if (sha384_vector(1, &key, &key_len, tk) < 0) + return -1; + key = tk; + key_len = 48; + } + + /* the HMAC_SHA384 transform looks like: + * + * SHA384(K XOR opad, SHA384(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 128 times + * opad is the byte 0x5c repeated 128 times + * and text is the data being protected */ + + /* start out by storing key in ipad */ + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with ipad values */ + for (i = 0; i < 128; i++) + k_pad[i] ^= 0x36; + + /* perform inner SHA384 */ + _addr[0] = k_pad; + _len[0] = 128; + for (i = 0; i < num_elem; i++) { + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; + } + if (sha384_vector(1 + num_elem, _addr, _len, mac) < 0) + return -1; + + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 128; i++) + k_pad[i] ^= 0x5c; + + /* perform outer SHA384 */ + _addr[0] = k_pad; + _len[0] = 128; + _addr[1] = mac; + _len[1] = SHA384_MAC_LEN; + return sha384_vector(2, _addr, _len, mac); +} + + +/** + * hmac_sha384 - HMAC-SHA384 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (48 bytes) + * Returns: 0 on success, -1 on failure + */ +int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac); +} diff --git a/src/crypto/sha384.h b/src/crypto/sha384.h index 3deafa59ec29..2241425385c3 100644 --- a/src/crypto/sha384.h +++ b/src/crypto/sha384.h @@ -1,6 +1,6 @@ /* * SHA384 hash implementation and interface functions - * Copyright (c) 2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2015-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -15,10 +15,13 @@ int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); -void sha384_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len); -void sha384_prf_bits(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, - size_t buf_len_bits); +int sha384_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +int sha384_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits); +int hmac_sha384_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen); #endif /* SHA384_H */ diff --git a/src/crypto/sha512-kdf.c b/src/crypto/sha512-kdf.c new file mode 100644 index 000000000000..8b71f9b0e4f9 --- /dev/null +++ b/src/crypto/sha512-kdf.c @@ -0,0 +1,87 @@ +/* + * HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869) + * Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha512.h" + + +/** + * hmac_sha512_kdf - HMAC-SHA512 based KDF (RFC 5295) + * @secret: Key for KDF + * @secret_len: Length of the key in bytes + * @label: A unique label for each purpose of the KDF or %NULL to select + * RFC 5869 HKDF-Expand() with arbitrary seed (= info) + * @seed: Seed value to bind into the key + * @seed_len: Length of the seed + * @out: Buffer for the generated pseudo-random key + * @outlen: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure. + * + * This function is used to derive new, cryptographically separate keys from a + * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used + * with label = NULL and seed = info, this matches HKDF-Expand() defined in + * RFC 5869, Chapter 2.3. + */ +int hmac_sha512_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen) +{ + u8 T[SHA512_MAC_LEN]; + u8 iter = 1; + const unsigned char *addr[4]; + size_t len[4]; + size_t pos, clen; + + addr[0] = T; + len[0] = SHA512_MAC_LEN; + if (label) { + addr[1] = (const unsigned char *) label; + len[1] = os_strlen(label) + 1; + } else { + addr[1] = (const u8 *) ""; + len[1] = 0; + } + addr[2] = seed; + len[2] = seed_len; + addr[3] = &iter; + len[3] = 1; + + if (hmac_sha512_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0) + return -1; + + pos = 0; + for (;;) { + clen = outlen - pos; + if (clen > SHA512_MAC_LEN) + clen = SHA512_MAC_LEN; + os_memcpy(out + pos, T, clen); + pos += clen; + + if (pos == outlen) + break; + + if (iter == 255) { + os_memset(out, 0, outlen); + os_memset(T, 0, SHA512_MAC_LEN); + return -1; + } + iter++; + + if (hmac_sha512_vector(secret, secret_len, 4, addr, len, T) < 0) + { + os_memset(out, 0, outlen); + os_memset(T, 0, SHA512_MAC_LEN); + return -1; + } + } + + os_memset(T, 0, SHA512_MAC_LEN); + return 0; +} diff --git a/src/crypto/sha512-prf.c b/src/crypto/sha512-prf.c new file mode 100644 index 000000000000..3b2ad889d6ef --- /dev/null +++ b/src/crypto/sha512-prf.c @@ -0,0 +1,108 @@ +/* + * SHA512-based KDF (IEEE 802.11ac) + * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha512.h" +#include "crypto.h" + + +/** + * sha512_prf - SHA512-based Key derivation function (IEEE 802.11ac, 11.6.1.7.2) + * @key: Key for KDF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure + * + * This function is used to derive new, cryptographically separate keys from a + * given key. + */ +int sha512_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + return sha512_prf_bits(key, key_len, label, data, data_len, buf, + buf_len * 8); +} + + +/** + * sha512_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function + * @key: Key for KDF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bits of key to generate + * Returns: 0 on success, -1 on failure + * + * This function is used to derive new, cryptographically separate keys from a + * given key. If the requested buf_len is not divisible by eight, the least + * significant 1-7 bits of the last octet in the output are not part of the + * requested output. + */ +int sha512_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits) +{ + u16 counter = 1; + size_t pos, plen; + u8 hash[SHA512_MAC_LEN]; + const u8 *addr[4]; + size_t len[4]; + u8 counter_le[2], length_le[2]; + size_t buf_len = (buf_len_bits + 7) / 8; + + addr[0] = counter_le; + len[0] = 2; + addr[1] = (u8 *) label; + len[1] = os_strlen(label); + addr[2] = data; + len[2] = data_len; + addr[3] = length_le; + len[3] = sizeof(length_le); + + WPA_PUT_LE16(length_le, buf_len_bits); + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + WPA_PUT_LE16(counter_le, counter); + if (plen >= SHA512_MAC_LEN) { + if (hmac_sha512_vector(key, key_len, 4, addr, len, + &buf[pos]) < 0) + return -1; + pos += SHA512_MAC_LEN; + } else { + if (hmac_sha512_vector(key, key_len, 4, addr, len, + hash) < 0) + return -1; + os_memcpy(&buf[pos], hash, plen); + pos += plen; + break; + } + counter++; + } + + /* + * Mask out unused bits in the last octet if it does not use all the + * bits. + */ + if (buf_len_bits % 8) { + u8 mask = 0xff << (8 - buf_len_bits % 8); + buf[pos - 1] &= mask; + } + + os_memset(hash, 0, sizeof(hash)); + + return 0; +} diff --git a/src/crypto/sha512.h b/src/crypto/sha512.h new file mode 100644 index 000000000000..8e64c8b116fa --- /dev/null +++ b/src/crypto/sha512.h @@ -0,0 +1,27 @@ +/* + * SHA512 hash implementation and interface functions + * Copyright (c) 2015-2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA512_H +#define SHA512_H + +#define SHA512_MAC_LEN 64 + +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac); +int sha512_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +int sha512_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits); +int hmac_sha512_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen); + +#endif /* SHA512_H */ diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 11d504a97fc0..481b34681d7b 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -41,6 +41,7 @@ enum tls_fail_reason { TLS_FAIL_SERVER_CHAIN_PROBE = 8, TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9, TLS_FAIL_DOMAIN_MISMATCH = 10, + TLS_FAIL_INSUFFICIENT_KEY_LEN = 11, }; @@ -63,6 +64,7 @@ union tls_event_data { size_t hash_len; const char *altsubject[TLS_MAX_ALT_SUBJECT]; int num_altsubject; + const char *serial_num; } peer_cert; struct { @@ -80,6 +82,7 @@ struct tls_config { int cert_in_cb; const char *openssl_ciphers; unsigned int tls_session_lifetime; + unsigned int tls_flags; void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); @@ -97,6 +100,9 @@ struct tls_config { #define TLS_CONN_DISABLE_TLSv1_0 BIT(8) #define TLS_CONN_EXT_CERT_CHECK BIT(9) #define TLS_CONN_REQUIRE_OCSP_ALL BIT(10) +#define TLS_CONN_SUITEB BIT(11) +#define TLS_CONN_SUITEB_NO_ECDH BIT(12) +#define TLS_CONN_DISABLE_TLSv1_3 BIT(13) /** * struct tls_connection_params - Parameters for TLS connection @@ -248,6 +254,18 @@ void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn); int tls_connection_established(void *tls_ctx, struct tls_connection *conn); /** + * tls_connection_peer_serial_num - Fetch peer certificate serial number + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: Allocated string buffer containing the peer certificate serial + * number or %NULL on error. + * + * The caller is responsible for freeing the returned buffer with os_free(). + */ +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn); + +/** * tls_connection_shutdown - Shutdown TLS connection * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c index 200f0eda931a..36dafd2603f0 100644 --- a/src/crypto/tls_gnutls.c +++ b/src/crypto/tls_gnutls.c @@ -1,6 +1,6 @@ /* * SSL/TLS interface functions for GnuTLS - * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -295,6 +295,14 @@ int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) } +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn) +{ + /* TODO */ + return NULL; +} + + int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) { struct tls_global *global = ssl_ctx; @@ -346,6 +354,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { int ret; + const char *err; + char prio_buf[100]; + const char *prio = NULL; if (conn == NULL || params == NULL) return -1; @@ -397,12 +408,60 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, conn->flags = params->flags; + if (params->flags & (TLS_CONN_DISABLE_TLSv1_0 | + TLS_CONN_DISABLE_TLSv1_1 | + TLS_CONN_DISABLE_TLSv1_2)) { + os_snprintf(prio_buf, sizeof(prio_buf), + "NORMAL:-VERS-SSL3.0%s%s%s", + params->flags & TLS_CONN_DISABLE_TLSv1_0 ? + ":-VERS-TLS1.0" : "", + params->flags & TLS_CONN_DISABLE_TLSv1_1 ? + ":-VERS-TLS1.1" : "", + params->flags & TLS_CONN_DISABLE_TLSv1_2 ? + ":-VERS-TLS1.2" : ""); + prio = prio_buf; + } + if (params->openssl_ciphers) { - wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported"); - return -1; + if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) { + prio = "SUITEB128"; + } else if (os_strcmp(params->openssl_ciphers, + "SUITEB192") == 0) { + prio = "SUITEB192"; + } else if ((params->flags & TLS_CONN_SUITEB) && + os_strcmp(params->openssl_ciphers, + "ECDHE-RSA-AES256-GCM-SHA384") == 0) { + prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL"; + } else if (os_strcmp(params->openssl_ciphers, + "ECDHE-RSA-AES256-GCM-SHA384") == 0) { + prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL"; + } else if (os_strcmp(params->openssl_ciphers, + "DHE-RSA-AES256-GCM-SHA384") == 0) { + prio = "NONE:+VERS-TLS1.2:+AEAD:+DHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL:%PROFILE_HIGH"; + } else if (os_strcmp(params->openssl_ciphers, + "ECDHE-ECDSA-AES256-GCM-SHA384") == 0) { + prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-ECDSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL"; + } else { + wpa_printf(MSG_INFO, + "GnuTLS: openssl_ciphers not supported"); + return -1; + } + } else if (params->flags & TLS_CONN_SUITEB) { + prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-ECDSA:+ECDHE-RSA:+DHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL:%PROFILE_HIGH"; } - /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); + if (prio) { + wpa_printf(MSG_DEBUG, "GnuTLS: Set priority string: %s", prio); + ret = gnutls_priority_set_direct(conn->session, prio, &err); + if (ret < 0) { + wpa_printf(MSG_ERROR, + "GnuTLS: Priority string failure at '%s'", + err); + return -1; + } + } + + /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); * to force peer validation(?) */ if (params->ca_cert) { @@ -425,6 +484,13 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, gnutls_strerror(ret)); return -1; } + wpa_printf(MSG_DEBUG, + "GnuTLS: Successfully read CA cert '%s' in PEM format", + params->ca_cert); + } else { + wpa_printf(MSG_DEBUG, + "GnuTLS: Successfully read CA cert '%s' in DER format", + params->ca_cert); } } else if (params->ca_cert_blob) { gnutls_datum_t ca; @@ -472,6 +538,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } if (params->client_cert && params->private_key) { + wpa_printf(MSG_DEBUG, + "GnuTLS: Try to parse client cert '%s' and key '%s' in DER format", + params->client_cert, params->private_key); #if GNUTLS_VERSION_NUMBER >= 0x03010b ret = gnutls_certificate_set_x509_key_file2( conn->xcred, params->client_cert, params->private_key, @@ -483,8 +552,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, GNUTLS_X509_FMT_DER); #endif if (ret < 0) { - wpa_printf(MSG_DEBUG, "Failed to read client cert/key " - "in DER format: %s", gnutls_strerror(ret)); + wpa_printf(MSG_DEBUG, + "GnuTLS: Failed to read client cert/key in DER format (%s) - try in PEM format", + gnutls_strerror(ret)); #if GNUTLS_VERSION_NUMBER >= 0x03010b ret = gnutls_certificate_set_x509_key_file2( conn->xcred, params->client_cert, @@ -501,11 +571,19 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, gnutls_strerror(ret)); return ret; } + wpa_printf(MSG_DEBUG, + "GnuTLS: Successfully read client cert/key in PEM format"); + } else { + wpa_printf(MSG_DEBUG, + "GnuTLS: Successfully read client cert/key in DER format"); } } else if (params->private_key) { int pkcs12_ok = 0; #ifdef PKCS12_FUNCS /* Try to load in PKCS#12 format */ + wpa_printf(MSG_DEBUG, + "GnuTLS: Try to parse client cert/key '%s'in PKCS#12 DER format", + params->private_key); ret = gnutls_certificate_set_x509_simple_pkcs12_file( conn->xcred, params->private_key, GNUTLS_X509_FMT_DER, params->private_key_passwd); @@ -1333,6 +1411,7 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, ret = gnutls_handshake(conn->session); if (ret < 0) { gnutls_alert_description_t alert; + union tls_event_data ev; switch (ret) { case GNUTLS_E_AGAIN: @@ -1343,14 +1422,29 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, conn->push_buf = wpabuf_alloc(0); } break; + case GNUTLS_E_DH_PRIME_UNACCEPTABLE: + wpa_printf(MSG_DEBUG, "GnuTLS: Unacceptable DH prime"); + if (conn->global->event_cb) { + os_memset(&ev, 0, sizeof(ev)); + ev.alert.is_local = 1; + ev.alert.type = "fatal"; + ev.alert.description = "insufficient security"; + conn->global->event_cb(conn->global->cb_ctx, + TLS_ALERT, &ev); + } + /* + * Could send a TLS Alert to the server, but for now, + * simply terminate handshake. + */ + conn->failed++; + conn->write_alerts++; + break; case GNUTLS_E_FATAL_ALERT_RECEIVED: alert = gnutls_alert_get(conn->session); wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert", __func__, gnutls_alert_get_name(alert)); conn->read_alerts++; if (conn->global->event_cb != NULL) { - union tls_event_data ev; - os_memset(&ev, 0, sizeof(ev)); ev.alert.is_local = 0; ev.alert.type = gnutls_alert_get_name(alert); @@ -1501,16 +1595,53 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, int tls_get_version(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { - /* TODO */ - return -1; + gnutls_protocol_t ver; + + ver = gnutls_protocol_get_version(conn->session); + if (ver == GNUTLS_TLS1_0) + os_strlcpy(buf, "TLSv1", buflen); + else if (ver == GNUTLS_TLS1_1) + os_strlcpy(buf, "TLSv1.1", buflen); + else if (ver == GNUTLS_TLS1_2) + os_strlcpy(buf, "TLSv1.2", buflen); + else + return -1; + return 0; } int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { - /* TODO */ - buf[0] = '\0'; + gnutls_cipher_algorithm_t cipher; + gnutls_kx_algorithm_t kx; + gnutls_mac_algorithm_t mac; + const char *kx_str, *cipher_str, *mac_str; + int res; + + cipher = gnutls_cipher_get(conn->session); + cipher_str = gnutls_cipher_get_name(cipher); + if (!cipher_str) + cipher_str = ""; + + kx = gnutls_kx_get(conn->session); + kx_str = gnutls_kx_get_name(kx); + if (!kx_str) + kx_str = ""; + + mac = gnutls_mac_get(conn->session); + mac_str = gnutls_mac_get_name(mac); + if (!mac_str) + mac_str = ""; + + if (kx == GNUTLS_KX_RSA) + res = os_snprintf(buf, buflen, "%s-%s", cipher_str, mac_str); + else + res = os_snprintf(buf, buflen, "%s-%s-%s", + kx_str, cipher_str, mac_str); + if (os_snprintf_error(buflen, res)) + return -1; + return 0; } diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c index c7cb5ded331f..d289c9442ceb 100644 --- a/src/crypto/tls_internal.c +++ b/src/crypto/tls_internal.c @@ -177,6 +177,14 @@ int tls_connection_established(void *tls_ctx, struct tls_connection *conn) } +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn) +{ + /* TODO */ + return NULL; +} + + int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) { #ifdef CONFIG_TLS_INTERNAL_CLIENT diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c index dd5681e9ca3c..5d0c6bda1547 100644 --- a/src/crypto/tls_none.c +++ b/src/crypto/tls_none.c @@ -45,6 +45,13 @@ int tls_connection_established(void *tls_ctx, struct tls_connection *conn) } +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn) +{ + return NULL; +} + + int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) { return -1; diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index 23ac64b48cd9..0d5ebda699f0 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -59,7 +59,8 @@ typedef int stack_index_t; #endif /* SSL_set_tlsext_status_type */ #if (OPENSSL_VERSION_NUMBER < 0x10100000L || \ - defined(LIBRESSL_VERSION_NUMBER)) && \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L)) && \ !defined(BORINGSSL_API_VERSION) /* * SSL_get_client_random() and SSL_get_server_random() were added in OpenSSL @@ -103,6 +104,21 @@ static size_t SSL_SESSION_get_master_key(const SSL_SESSION *session, #endif +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#ifdef CONFIG_SUITEB +static int RSA_bits(const RSA *r) +{ + return BN_num_bits(r->n); +} +#endif /* CONFIG_SUITEB */ + + +static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x) +{ + return ASN1_STRING_data((ASN1_STRING *) x); +} +#endif + #ifdef ANDROID #include <openssl/pem.h> #include <keystore/keystore_get.h> @@ -222,6 +238,8 @@ struct tls_connection { unsigned int server_cert_only:1; unsigned int invalid_hb_used:1; unsigned int success_data:1; + unsigned int client_hello_generated:1; + unsigned int server:1; u8 srv_cert_hash[32]; @@ -233,6 +251,9 @@ struct tls_connection { unsigned char client_random[SSL3_RANDOM_SIZE]; unsigned char server_random[SSL3_RANDOM_SIZE]; + + u16 cipher_suite; + int server_dh_prime_len; }; @@ -919,7 +940,9 @@ void * tls_init(const struct tls_config *conf) } #endif /* OPENSSL_FIPS */ #endif /* CONFIG_FIPS */ -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) SSL_load_error_strings(); SSL_library_init(); #ifndef OPENSSL_NO_SHA256 @@ -972,6 +995,14 @@ void * tls_init(const struct tls_config *conf) SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3); +#ifdef SSL_MODE_NO_AUTO_CHAIN + /* Number of deployed use cases assume the default OpenSSL behavior of + * auto chaining the local certificate is in use. BoringSSL removed this + * functionality by default, so we need to restore it here to avoid + * breaking existing use cases. */ + SSL_CTX_clear_mode(ssl, SSL_MODE_NO_AUTO_CHAIN); +#endif /* SSL_MODE_NO_AUTO_CHAIN */ + SSL_CTX_set_info_callback(ssl, ssl_info_cb); SSL_CTX_set_app_data(ssl, context); if (data->tls_session_lifetime > 0) { @@ -999,8 +1030,10 @@ void * tls_init(const struct tls_config *conf) #ifndef OPENSSL_NO_ENGINE wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); +#if OPENSSL_VERSION_NUMBER < 0x10100000L ERR_load_ENGINE_strings(); ENGINE_load_dynamic(); +#endif /* OPENSSL_VERSION_NUMBER */ if (conf && (conf->opensc_engine_path || conf->pkcs11_engine_path || @@ -1017,7 +1050,7 @@ void * tls_init(const struct tls_config *conf) if (conf && conf->openssl_ciphers) ciphers = conf->openssl_ciphers; else - ciphers = "DEFAULT:!EXP:!LOW"; + ciphers = TLS_DEFAULT_CIPHERS; if (SSL_CTX_set_cipher_list(ssl, ciphers) != 1) { wpa_printf(MSG_ERROR, "OpenSSL: Failed to set cipher string '%s'", @@ -1043,7 +1076,9 @@ void tls_deinit(void *ssl_ctx) tls_openssl_ref_count--; if (tls_openssl_ref_count == 0) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) #ifndef OPENSSL_NO_ENGINE ENGINE_cleanup(); #endif /* OPENSSL_NO_ENGINE */ @@ -1296,6 +1331,95 @@ static const char * openssl_handshake_type(int content_type, const u8 *buf, } +#ifdef CONFIG_SUITEB + +static void check_server_hello(struct tls_connection *conn, + const u8 *pos, const u8 *end) +{ + size_t payload_len, id_len; + + /* + * Parse ServerHello to get the selected cipher suite since OpenSSL does + * not make it cleanly available during handshake and we need to know + * whether DHE was selected. + */ + + if (end - pos < 3) + return; + payload_len = WPA_GET_BE24(pos); + pos += 3; + + if ((size_t) (end - pos) < payload_len) + return; + end = pos + payload_len; + + /* Skip Version and Random */ + if (end - pos < 2 + SSL3_RANDOM_SIZE) + return; + pos += 2 + SSL3_RANDOM_SIZE; + + /* Skip Session ID */ + if (end - pos < 1) + return; + id_len = *pos++; + if ((size_t) (end - pos) < id_len) + return; + pos += id_len; + + if (end - pos < 2) + return; + conn->cipher_suite = WPA_GET_BE16(pos); + wpa_printf(MSG_DEBUG, "OpenSSL: Server selected cipher suite 0x%x", + conn->cipher_suite); +} + + +static void check_server_key_exchange(SSL *ssl, struct tls_connection *conn, + const u8 *pos, const u8 *end) +{ + size_t payload_len; + u16 dh_len; + BIGNUM *p; + int bits; + + if (!(conn->flags & TLS_CONN_SUITEB)) + return; + + /* DHE is enabled only with DHE-RSA-AES256-GCM-SHA384 */ + if (conn->cipher_suite != 0x9f) + return; + + if (end - pos < 3) + return; + payload_len = WPA_GET_BE24(pos); + pos += 3; + + if ((size_t) (end - pos) < payload_len) + return; + end = pos + payload_len; + + if (end - pos < 2) + return; + dh_len = WPA_GET_BE16(pos); + pos += 2; + + if ((size_t) (end - pos) < dh_len) + return; + p = BN_bin2bn(pos, dh_len, NULL); + if (!p) + return; + + bits = BN_num_bits(p); + BN_free(p); + + conn->server_dh_prime_len = bits; + wpa_printf(MSG_DEBUG, "OpenSSL: Server DH prime length: %d bits", + conn->server_dh_prime_len); +} + +#endif /* CONFIG_SUITEB */ + + static void tls_msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg) { @@ -1322,6 +1446,18 @@ static void tls_msg_cb(int write_p, int version, int content_type, conn->invalid_hb_used = 1; } } + +#ifdef CONFIG_SUITEB + /* + * Need to parse these handshake messages to be able to check DH prime + * length since OpenSSL does not expose the new cipher suite and DH + * parameters during handshake (e.g., for cert_cb() callback). + */ + if (content_type == 22 && pos && len > 0 && pos[0] == 2) + check_server_hello(conn, pos + 1, pos + len); + if (content_type == 22 && pos && len > 0 && pos[0] == 12) + check_server_key_exchange(ssl, conn, pos + 1, pos + len); +#endif /* CONFIG_SUITEB */ } @@ -1410,6 +1546,31 @@ int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) } +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn) +{ + ASN1_INTEGER *ser; + char *serial_num; + size_t len; + + if (!conn->peer_cert) + return NULL; + + ser = X509_get_serialNumber(conn->peer_cert); + if (!ser) + return NULL; + + len = ASN1_STRING_length(ser) * 2 + 1; + serial_num = os_malloc(len); + if (!serial_num) + return NULL; + wpa_snprintf_hex_uppercase(serial_num, len, + ASN1_STRING_get0_data(ser), + ASN1_STRING_length(ser)); + return serial_num; +} + + int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) @@ -1694,6 +1855,8 @@ static void openssl_tls_cert_event(struct tls_connection *conn, GENERAL_NAME *gen; void *ext; stack_index_t i; + ASN1_INTEGER *ser; + char serial_num[128]; #ifdef CONFIG_SHA256 u8 hash[32]; #endif /* CONFIG_SHA256 */ @@ -1722,6 +1885,14 @@ static void openssl_tls_cert_event(struct tls_connection *conn, ev.peer_cert.depth = depth; ev.peer_cert.subject = subject; + ser = X509_get_serialNumber(err_cert); + if (ser) { + wpa_snprintf_hex_uppercase(serial_num, sizeof(serial_num), + ASN1_STRING_get0_data(ser), + ASN1_STRING_length(ser)); + ev.peer_cert.serial_num = serial_num; + } + ext = X509_get_ext_d2i(err_cert, NID_subject_alt_name, NULL, NULL); for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { char *pos; @@ -1916,6 +2087,37 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) TLS_FAIL_SERVER_CHAIN_PROBE); } +#ifdef CONFIG_SUITEB + if (conn->flags & TLS_CONN_SUITEB) { + EVP_PKEY *pk; + RSA *rsa; + int len = -1; + + pk = X509_get_pubkey(err_cert); + if (pk) { + rsa = EVP_PKEY_get1_RSA(pk); + if (rsa) { + len = RSA_bits(rsa); + RSA_free(rsa); + } + EVP_PKEY_free(pk); + } + + if (len >= 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: RSA modulus size: %d bits", len); + if (len < 3072) { + preverify_ok = 0; + openssl_tls_fail_event( + conn, err_cert, err, + depth, buf, + "Insufficient RSA modulus size", + TLS_FAIL_INSUFFICIENT_KEY_LEN); + } + } + } +#endif /* CONFIG_SUITEB */ + #ifdef OPENSSL_IS_BORINGSSL if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) && preverify_ok) { @@ -2249,15 +2451,48 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, } -static void tls_set_conn_flags(SSL *ssl, unsigned int flags) +#ifdef CONFIG_SUITEB +#if OPENSSL_VERSION_NUMBER >= 0x10002000L +static int suiteb_cert_cb(SSL *ssl, void *arg) +{ + struct tls_connection *conn = arg; + + /* + * This cert_cb() is not really the best location for doing a + * constraint check for the ServerKeyExchange message, but this seems to + * be the only place where the current OpenSSL sequence can be + * terminated cleanly with an TLS alert going out to the server. + */ + + if (!(conn->flags & TLS_CONN_SUITEB)) + return 1; + + /* DHE is enabled only with DHE-RSA-AES256-GCM-SHA384 */ + if (conn->cipher_suite != 0x9f) + return 1; + + if (conn->server_dh_prime_len >= 3072) + return 1; + + wpa_printf(MSG_DEBUG, + "OpenSSL: Server DH prime length (%d bits) not sufficient for Suite B RSA - reject handshake", + conn->server_dh_prime_len); + return 0; +} +#endif /* OPENSSL_VERSION_NUMBER */ +#endif /* CONFIG_SUITEB */ + + +static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags, + const char *openssl_ciphers) { + SSL *ssl = conn->ssl; + #ifdef SSL_OP_NO_TICKET if (flags & TLS_CONN_DISABLE_SESSION_TICKET) SSL_set_options(ssl, SSL_OP_NO_TICKET); -#ifdef SSL_clear_options else SSL_clear_options(ssl, SSL_OP_NO_TICKET); -#endif /* SSL_clear_options */ #endif /* SSL_OP_NO_TICKET */ #ifdef SSL_OP_NO_TLSv1 @@ -2278,6 +2513,118 @@ static void tls_set_conn_flags(SSL *ssl, unsigned int flags) else SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2); #endif /* SSL_OP_NO_TLSv1_2 */ +#ifdef SSL_OP_NO_TLSv1_3 + if (flags & TLS_CONN_DISABLE_TLSv1_3) + SSL_set_options(ssl, SSL_OP_NO_TLSv1_3); + else + SSL_clear_options(ssl, SSL_OP_NO_TLSv1_3); +#endif /* SSL_OP_NO_TLSv1_3 */ +#ifdef CONFIG_SUITEB +#ifdef OPENSSL_IS_BORINGSSL + /* Start with defaults from BoringSSL */ + SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, NULL, 0); +#endif /* OPENSSL_IS_BORINGSSL */ +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + if (flags & TLS_CONN_SUITEB_NO_ECDH) { + const char *ciphers = "DHE-RSA-AES256-GCM-SHA384"; + + if (openssl_ciphers) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Override ciphers for Suite B (no ECDH): %s", + openssl_ciphers); + ciphers = openssl_ciphers; + } + if (SSL_set_cipher_list(ssl, ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B ciphers"); + return -1; + } + } else if (flags & TLS_CONN_SUITEB) { + EC_KEY *ecdh; + const char *ciphers = + "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384"; + int nid[1] = { NID_secp384r1 }; + + if (openssl_ciphers) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Override ciphers for Suite B: %s", + openssl_ciphers); + ciphers = openssl_ciphers; + } + if (SSL_set_cipher_list(ssl, ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B ciphers"); + return -1; + } + + if (SSL_set1_curves(ssl, nid, 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B curves"); + return -1; + } + + ecdh = EC_KEY_new_by_curve_name(NID_secp384r1); + if (!ecdh || SSL_set_tmp_ecdh(ssl, ecdh) != 1) { + EC_KEY_free(ecdh); + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH parameter"); + return -1; + } + EC_KEY_free(ecdh); + } + if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) { +#ifdef OPENSSL_IS_BORINGSSL + uint16_t sigalgs[1] = { SSL_SIGN_RSA_PKCS1_SHA384 }; + + if (SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, sigalgs, + 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B sigalgs"); + return -1; + } +#else /* OPENSSL_IS_BORINGSSL */ + /* ECDSA+SHA384 if need to add EC support here */ + if (SSL_set1_sigalgs_list(ssl, "RSA+SHA384") != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B sigalgs"); + return -1; + } +#endif /* OPENSSL_IS_BORINGSSL */ + + SSL_set_options(ssl, SSL_OP_NO_TLSv1); + SSL_set_options(ssl, SSL_OP_NO_TLSv1_1); + SSL_set_cert_cb(ssl, suiteb_cert_cb, conn); + } +#else /* OPENSSL_VERSION_NUMBER < 0x10002000L */ + if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) { + wpa_printf(MSG_ERROR, + "OpenSSL: Suite B RSA case not supported with this OpenSSL version"); + return -1; + } +#endif /* OPENSSL_VERSION_NUMBER */ + +#ifdef OPENSSL_IS_BORINGSSL + if (openssl_ciphers && os_strcmp(openssl_ciphers, "SUITEB192") == 0) { + uint16_t sigalgs[1] = { SSL_SIGN_ECDSA_SECP384R1_SHA384 }; + int nid[1] = { NID_secp384r1 }; + + if (SSL_set1_curves(ssl, nid, 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B curves"); + return -1; + } + + if (SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, sigalgs, + 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B sigalgs"); + return -1; + } + } +#endif /* OPENSSL_IS_BORINGSSL */ +#endif /* CONFIG_SUITEB */ + + return 0; } @@ -2301,7 +2648,8 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); } - tls_set_conn_flags(conn->ssl, flags); + if (tls_set_conn_flags(conn, flags, NULL) < 0) + return -1; conn->flags = flags; SSL_set_accept_state(conn->ssl); @@ -2334,7 +2682,7 @@ static int tls_connection_client_cert(struct tls_connection *conn, return 0; #ifdef PKCS12_FUNCS -#if OPENSSL_VERSION_NUMBER < 0x10002000L +#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER) /* * Clear previously set extra chain certificates, if any, from PKCS#12 * processing in tls_parse_pkcs12() to allow OpenSSL to build a new @@ -2365,13 +2713,24 @@ static int tls_connection_client_cert(struct tls_connection *conn, int ret = -1; if (bio) { x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); - BIO_free(bio); } if (x509) { if (SSL_use_certificate(conn->ssl, x509) == 1) ret = 0; X509_free(x509); } + + /* Read additional certificates into the chain. */ + while (bio) { + x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); + if (x509) { + /* Takes ownership of x509 */ + SSL_add0_chain_cert(conn->ssl, x509); + } else { + BIO_free(bio); + bio = NULL; + } + } return ret; } #endif /* ANDROID */ @@ -2430,16 +2789,6 @@ static int tls_global_client_cert(struct tls_data *data, } -static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) -{ - if (password == NULL) { - return 0; - } - os_strlcpy(buf, (char *) password, size); - return os_strlen(buf); -} - - #ifdef PKCS12_FUNCS static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12, const char *passwd) @@ -2758,6 +3107,64 @@ static int tls_connection_engine_private_key(struct tls_connection *conn) } +#ifndef OPENSSL_NO_STDIO +static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) +{ + if (!password) + return 0; + os_strlcpy(buf, (const char *) password, size); + return os_strlen(buf); +} +#endif /* OPENSSL_NO_STDIO */ + + +static int tls_use_private_key_file(struct tls_data *data, SSL *ssl, + const char *private_key, + const char *private_key_passwd) +{ +#ifndef OPENSSL_NO_STDIO + BIO *bio; + EVP_PKEY *pkey; + int ret; + + /* First try ASN.1 (DER). */ + bio = BIO_new_file(private_key, "r"); + if (!bio) + return -1; + pkey = d2i_PrivateKey_bio(bio, NULL); + BIO_free(bio); + + if (pkey) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s (DER) --> loaded", __func__); + } else { + /* Try PEM with the provided password. */ + bio = BIO_new_file(private_key, "r"); + if (!bio) + return -1; + pkey = PEM_read_bio_PrivateKey(bio, NULL, tls_passwd_cb, + (void *) private_key_passwd); + BIO_free(bio); + if (!pkey) + return -1; + wpa_printf(MSG_DEBUG, "OpenSSL: %s (PEM) --> loaded", __func__); + /* Clear errors from the previous failed load. */ + ERR_clear_error(); + } + + if (ssl) + ret = SSL_use_PrivateKey(ssl, pkey); + else + ret = SSL_CTX_use_PrivateKey(data->ssl, pkey); + + EVP_PKEY_free(pkey); + return ret == 1 ? 0 : -1; +#else /* OPENSSL_NO_STDIO */ + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); + return -1; +#endif /* OPENSSL_NO_STDIO */ +} + + static int tls_connection_private_key(struct tls_data *data, struct tls_connection *conn, const char *private_key, @@ -2765,23 +3172,11 @@ static int tls_connection_private_key(struct tls_data *data, const u8 *private_key_blob, size_t private_key_blob_len) { - SSL_CTX *ssl_ctx = data->ssl; - char *passwd; int ok; if (private_key == NULL && private_key_blob == NULL) return 0; - if (private_key_passwd) { - passwd = os_strdup(private_key_passwd); - if (passwd == NULL) - return -1; - } else - passwd = NULL; - - SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); - SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); - ok = 0; while (private_key_blob) { if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl, @@ -2812,7 +3207,8 @@ static int tls_connection_private_key(struct tls_data *data, } if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob, - private_key_blob_len, passwd) == 0) { + private_key_blob_len, + private_key_passwd) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> " "OK"); ok = 1; @@ -2823,29 +3219,14 @@ static int tls_connection_private_key(struct tls_data *data, } while (!ok && private_key) { -#ifndef OPENSSL_NO_STDIO - if (SSL_use_PrivateKey_file(conn->ssl, private_key, - SSL_FILETYPE_ASN1) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: " - "SSL_use_PrivateKey_File (DER) --> OK"); + if (tls_use_private_key_file(data, conn->ssl, private_key, + private_key_passwd) == 0) { ok = 1; break; } - if (SSL_use_PrivateKey_file(conn->ssl, private_key, - SSL_FILETYPE_PEM) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: " - "SSL_use_PrivateKey_File (PEM) --> OK"); - ok = 1; - break; - } -#else /* OPENSSL_NO_STDIO */ - wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", - __func__); -#endif /* OPENSSL_NO_STDIO */ - - if (tls_read_pkcs12(data, conn->ssl, private_key, passwd) - == 0) { + if (tls_read_pkcs12(data, conn->ssl, private_key, + private_key_passwd) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file " "--> OK"); ok = 1; @@ -2865,12 +3246,9 @@ static int tls_connection_private_key(struct tls_data *data, if (!ok) { tls_show_errors(MSG_INFO, __func__, "Failed to load private key"); - os_free(passwd); return -1; } ERR_clear_error(); - SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); - os_free(passwd); if (!SSL_check_private_key(conn->ssl)) { tls_show_errors(MSG_INFO, __func__, "Private key failed " @@ -2888,37 +3266,19 @@ static int tls_global_private_key(struct tls_data *data, const char *private_key_passwd) { SSL_CTX *ssl_ctx = data->ssl; - char *passwd; if (private_key == NULL) return 0; - if (private_key_passwd) { - passwd = os_strdup(private_key_passwd); - if (passwd == NULL) - return -1; - } else - passwd = NULL; - - SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); - SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); - if ( -#ifndef OPENSSL_NO_STDIO - SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, - SSL_FILETYPE_ASN1) != 1 && - SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, - SSL_FILETYPE_PEM) != 1 && -#endif /* OPENSSL_NO_STDIO */ - tls_read_pkcs12(data, NULL, private_key, passwd)) { + if (tls_use_private_key_file(data, NULL, private_key, + private_key_passwd) && + tls_read_pkcs12(data, NULL, private_key, private_key_passwd)) { tls_show_errors(MSG_INFO, __func__, "Failed to load private key"); - os_free(passwd); ERR_clear_error(); return -1; } - os_free(passwd); ERR_clear_error(); - SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); if (!SSL_CTX_check_private_key(ssl_ctx)) { tls_show_errors(MSG_INFO, __func__, @@ -3105,7 +3465,9 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, #ifdef OPENSSL_NEED_EAP_FAST_PRF static int openssl_get_keyblock_size(SSL *ssl) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) const EVP_CIPHER *c; const EVP_MD *h; int md_size; @@ -3252,8 +3614,7 @@ int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, static struct wpabuf * -openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, - int server) +openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data) { int res; struct wpabuf *out_data; @@ -3271,7 +3632,7 @@ openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, } /* Initiate TLS handshake or continue the existing handshake */ - if (server) + if (conn->server) res = SSL_accept(conn->ssl); else res = SSL_connect(conn->ssl); @@ -3286,8 +3647,57 @@ openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, else { tls_show_errors(MSG_INFO, __func__, "SSL_connect"); conn->failed++; + if (!conn->server && !conn->client_hello_generated) { + /* The server would not understand TLS Alert + * before ClientHello, so simply terminate + * handshake on this type of error case caused + * by a likely internal error like no ciphers + * available. */ + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not generate ClientHello"); + conn->write_alerts++; + return NULL; + } + } + } + + if (!conn->server && !conn->failed) + conn->client_hello_generated = 1; + +#ifdef CONFIG_SUITEB + if ((conn->flags & TLS_CONN_SUITEB) && !conn->server && + os_strncmp(SSL_get_cipher(conn->ssl), "DHE-", 4) == 0 && + conn->server_dh_prime_len < 3072) { + struct tls_context *context = conn->context; + + /* + * This should not be reached since earlier cert_cb should have + * terminated the handshake. Keep this check here for extra + * protection if anything goes wrong with the more low-level + * checks based on having to parse the TLS handshake messages. + */ + wpa_printf(MSG_DEBUG, + "OpenSSL: Server DH prime length: %d bits", + conn->server_dh_prime_len); + + if (context->event_cb) { + union tls_event_data ev; + + os_memset(&ev, 0, sizeof(ev)); + ev.alert.is_local = 1; + ev.alert.type = "fatal"; + ev.alert.description = "insufficient security"; + context->event_cb(context->cb_ctx, TLS_ALERT, &ev); } + /* + * Could send a TLS Alert to the server, but for now, simply + * terminate handshake. + */ + conn->failed++; + conn->write_alerts++; + return NULL; } +#endif /* CONFIG_SUITEB */ /* Get the TLS handshake data to be sent to the server */ res = BIO_ctrl_pending(conn->ssl_out); @@ -3358,14 +3768,14 @@ openssl_get_appl_data(struct tls_connection *conn, size_t max_len) static struct wpabuf * openssl_connection_handshake(struct tls_connection *conn, const struct wpabuf *in_data, - struct wpabuf **appl_data, int server) + struct wpabuf **appl_data) { struct wpabuf *out_data; if (appl_data) *appl_data = NULL; - out_data = openssl_handshake(conn, in_data, server); + out_data = openssl_handshake(conn, in_data); if (out_data == NULL) return NULL; if (conn->invalid_hb_used) { @@ -3402,7 +3812,7 @@ tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data) { - return openssl_connection_handshake(conn, in_data, appl_data, 0); + return openssl_connection_handshake(conn, in_data, appl_data); } @@ -3411,7 +3821,8 @@ struct wpabuf * tls_connection_server_handshake(void *tls_ctx, const struct wpabuf *in_data, struct wpabuf **appl_data) { - return openssl_connection_handshake(conn, in_data, appl_data, 1); + conn->server = 1; + return openssl_connection_handshake(conn, in_data, appl_data); } @@ -3506,7 +3917,7 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx, int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) { - return conn ? SSL_cache_hit(conn->ssl) : 0; + return conn ? SSL_session_reused(conn->ssl) : 0; } @@ -3747,7 +4158,7 @@ static int ocsp_resp_cb(SSL *s, void *arg) { struct tls_connection *conn = arg; const unsigned char *p; - int len, status, reason; + int len, status, reason, res; OCSP_RESPONSE *rsp; OCSP_BASICRESP *basic; OCSP_CERTID *id; @@ -3842,16 +4253,33 @@ static int ocsp_resp_cb(SSL *s, void *arg) return 0; } - id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer); + id = OCSP_cert_to_id(EVP_sha256(), conn->peer_cert, conn->peer_issuer); if (!id) { - wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier"); + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not create OCSP certificate identifier (SHA256)"); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(rsp); return 0; } - if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, - &this_update, &next_update)) { + res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, + &this_update, &next_update); + if (!res) { + id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer); + if (!id) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not create OCSP certificate identifier (SHA1)"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + res = OCSP_resp_find_status(basic, id, &status, &reason, + &produced_at, &this_update, + &next_update); + } + + if (!res) { wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s", (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" : " (OCSP not required)"); @@ -3935,6 +4363,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const char *cert_id = params->cert_id; const char *ca_cert_id = params->ca_cert_id; const char *engine_id = params->engine ? params->engine_id : NULL; + const char *ciphers; if (conn == NULL) return -1; @@ -3976,7 +4405,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, engine_id = "pkcs11"; #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) if (params->flags & TLS_CONN_EAP_FAST) { wpa_printf(MSG_DEBUG, "OpenSSL: Use TLSv1_method() for EAP-FAST"); @@ -3987,6 +4416,17 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } } #endif +#if OPENSSL_VERSION_NUMBER >= 0x10101000L +#ifdef SSL_OP_NO_TLSv1_3 + if (params->flags & TLS_CONN_EAP_FAST) { + /* Need to disable TLS v1.3 at least for now since OpenSSL 1.1.1 + * refuses to start the handshake with the modified ciphersuite + * list (no TLS v1.3 ciphersuites included) for EAP-FAST. */ + wpa_printf(MSG_DEBUG, "OpenSSL: Disable TLSv1.3 for EAP-FAST"); + SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_3); + } +#endif /* SSL_OP_NO_TLSv1_3 */ +#endif #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ while ((err = ERR_get_error())) { @@ -4045,15 +4485,27 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } - if (params->openssl_ciphers && - SSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) { + ciphers = params->openssl_ciphers; +#ifdef CONFIG_SUITEB +#ifdef OPENSSL_IS_BORINGSSL + if (ciphers && os_strcmp(ciphers, "SUITEB192") == 0) { + /* BoringSSL removed support for SUITEB192, so need to handle + * this with hardcoded ciphersuite and additional checks for + * other parameters. */ + ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384"; + } +#endif /* OPENSSL_IS_BORINGSSL */ +#endif /* CONFIG_SUITEB */ + if (ciphers && SSL_set_cipher_list(conn->ssl, ciphers) != 1) { wpa_printf(MSG_INFO, "OpenSSL: Failed to set cipher string '%s'", - params->openssl_ciphers); + ciphers); return -1; } - tls_set_conn_flags(conn->ssl, params->flags); + if (tls_set_conn_flags(conn, params->flags, + params->openssl_ciphers) < 0) + return -1; #ifdef OPENSSL_IS_BORINGSSL if (params->flags & TLS_CONN_REQUEST_OCSP) { @@ -4120,10 +4572,8 @@ int tls_global_set_params(void *tls_ctx, #ifdef SSL_OP_NO_TICKET if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET); -#ifdef SSL_CTX_clear_options else SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET); -#endif /* SSL_clear_options */ #endif /* SSL_OP_NO_TICKET */ #ifdef HAVE_OCSP @@ -4159,7 +4609,9 @@ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, struct tls_connection *conn = arg; int ret; -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) if (conn == NULL || conn->session_ticket_cb == NULL) return 0; @@ -4212,11 +4664,10 @@ static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data, wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket " "extension", data, len); - conn->session_ticket = os_malloc(len); + conn->session_ticket = os_memdup(data, len); if (conn->session_ticket == NULL) return 0; - os_memcpy(conn->session_ticket, data, len); conn->session_ticket_len = len; return 1; diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c new file mode 100644 index 000000000000..cc8c704466d2 --- /dev/null +++ b/src/crypto/tls_wolfssl.c @@ -0,0 +1,2175 @@ +/* + * SSL/TLS interface functions for wolfSSL TLS case + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "tls.h" + +/* wolfSSL includes */ +#include <wolfssl/options.h> +#include <wolfssl/ssl.h> +#include <wolfssl/error-ssl.h> +#include <wolfssl/wolfcrypt/asn.h> + +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) +#define HAVE_AESGCM +#include <wolfssl/wolfcrypt/aes.h> +#endif + +#if !defined(CONFIG_FIPS) && \ + (defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \ + defined(EAP_SERVER_FAST)) +#define WOLFSSL_NEED_EAP_FAST_PRF +#endif + +#define SECRET_LEN 48 +#define RAN_LEN 32 +#define SESSION_TICKET_LEN 256 + +static int tls_ref_count = 0; + +static int tls_ex_idx_session = 0; + + +/* tls input data for wolfSSL Read Callback */ +struct tls_in_data { + const struct wpabuf *in_data; + size_t consumed; /* how many bytes have we used already */ +}; + +/* tls output data for wolfSSL Write Callback */ +struct tls_out_data { + struct wpabuf *out_data; +}; + +struct tls_context { + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; + int cert_in_cb; + char *ocsp_stapling_response; +}; + +static struct tls_context *tls_global = NULL; + +/* wolfssl tls_connection */ +struct tls_connection { + struct tls_context *context; + WOLFSSL *ssl; + int read_alerts; + int write_alerts; + int failed; + struct tls_in_data input; + struct tls_out_data output; + char *subject_match; + char *alt_subject_match; + char *suffix_match; + char *domain_match; + + u8 srv_cert_hash[32]; + + unsigned char client_random[RAN_LEN]; + unsigned char server_random[RAN_LEN]; + unsigned int flags; +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) + tls_session_ticket_cb session_ticket_cb; + void *session_ticket_cb_ctx; + byte session_ticket[SESSION_TICKET_LEN]; +#endif + unsigned int ca_cert_verify:1; + unsigned int cert_probe:1; + unsigned int server_cert_only:1; + unsigned int success_data:1; + + WOLFSSL_X509 *peer_cert; + WOLFSSL_X509 *peer_issuer; + WOLFSSL_X509 *peer_issuer_issuer; +}; + + +static struct tls_context * tls_context_new(const struct tls_config *conf) +{ + struct tls_context *context = os_zalloc(sizeof(*context)); + + if (!context) + return NULL; + + if (conf) { + context->event_cb = conf->event_cb; + context->cb_ctx = conf->cb_ctx; + context->cert_in_cb = conf->cert_in_cb; + } + + return context; +} + + +static void wolfssl_reset_in_data(struct tls_in_data *in, + const struct wpabuf *buf) +{ + /* old one not owned by us so don't free */ + in->in_data = buf; + in->consumed = 0; +} + + +static void wolfssl_reset_out_data(struct tls_out_data *out) +{ + /* old one not owned by us so don't free */ + out->out_data = wpabuf_alloc_copy("", 0); +} + + +/* wolfSSL I/O Receive CallBack */ +static int wolfssl_receive_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx) +{ + size_t get = sz; + struct tls_in_data *data = ctx; + + if (!data) + return -1; + + if (get > (wpabuf_len(data->in_data) - data->consumed)) + get = wpabuf_len(data->in_data) - data->consumed; + + os_memcpy(buf, wpabuf_head(data->in_data) + data->consumed, get); + data->consumed += get; + + if (get == 0) + return -2; /* WANT_READ */ + + return (int) get; +} + + +/* wolfSSL I/O Send CallBack */ +static int wolfssl_send_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx) +{ + struct wpabuf *tmp; + struct tls_out_data *data = ctx; + + if (!data) + return -1; + + wpa_printf(MSG_DEBUG, "SSL: adding %d bytes", sz); + + tmp = wpabuf_alloc_copy(buf, sz); + if (!tmp) + return -1; + data->out_data = wpabuf_concat(data->out_data, tmp); + if (!data->out_data) + return -1; + + return sz; +} + + +static void remove_session_cb(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *sess) +{ + struct wpabuf *buf; + + buf = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + if (!buf) + return; + wpa_printf(MSG_DEBUG, + "wolfSSL: Free application session data %p (sess %p)", + buf, sess); + wpabuf_free(buf); + + wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL); +} + + +void * tls_init(const struct tls_config *conf) +{ + WOLFSSL_CTX *ssl_ctx; + struct tls_context *context; + const char *ciphers; + +#ifdef DEBUG_WOLFSSL + wolfSSL_Debugging_ON(); +#endif /* DEBUG_WOLFSSL */ + + context = tls_context_new(conf); + if (!context) + return NULL; + + if (tls_ref_count == 0) { + tls_global = context; + + if (wolfSSL_Init() < 0) + return NULL; + /* wolfSSL_Debugging_ON(); */ + } + + tls_ref_count++; + + /* start as client */ + ssl_ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()); + if (!ssl_ctx) { + tls_ref_count--; + if (context != tls_global) + os_free(context); + if (tls_ref_count == 0) { + os_free(tls_global); + tls_global = NULL; + } + } + wolfSSL_SetIORecv(ssl_ctx, wolfssl_receive_cb); + wolfSSL_SetIOSend(ssl_ctx, wolfssl_send_cb); + wolfSSL_CTX_set_ex_data(ssl_ctx, 0, context); + + if (conf->tls_session_lifetime > 0) { + wolfSSL_CTX_set_quiet_shutdown(ssl_ctx, 1); + wolfSSL_CTX_set_session_cache_mode(ssl_ctx, + SSL_SESS_CACHE_SERVER); + wolfSSL_CTX_set_timeout(ssl_ctx, conf->tls_session_lifetime); + wolfSSL_CTX_sess_set_remove_cb(ssl_ctx, remove_session_cb); + } else { + wolfSSL_CTX_set_session_cache_mode(ssl_ctx, + SSL_SESS_CACHE_CLIENT); + } + + if (conf && conf->openssl_ciphers) + ciphers = conf->openssl_ciphers; + else + ciphers = "ALL"; + if (wolfSSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) { + wpa_printf(MSG_ERROR, + "wolfSSL: Failed to set cipher string '%s'", + ciphers); + tls_deinit(ssl_ctx); + return NULL; + } + + return ssl_ctx; +} + + +void tls_deinit(void *ssl_ctx) +{ + struct tls_context *context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0); + + if (context != tls_global) + os_free(context); + + wolfSSL_CTX_free((WOLFSSL_CTX *) ssl_ctx); + + tls_ref_count--; + if (tls_ref_count == 0) { + wolfSSL_Cleanup(); + os_free(tls_global); + tls_global = NULL; + } +} + + +int tls_get_errors(void *tls_ctx) +{ +#ifdef DEBUG_WOLFSSL +#if 0 + unsigned long err; + + err = wolfSSL_ERR_peek_last_error_line(NULL, NULL); + if (err != 0) { + wpa_printf(MSG_INFO, "TLS - SSL error: %s", + wolfSSL_ERR_error_string(err, NULL)); + return 1; + } +#endif +#endif /* DEBUG_WOLFSSL */ + return 0; +} + + +struct tls_connection * tls_connection_init(void *tls_ctx) +{ + WOLFSSL_CTX *ssl_ctx = tls_ctx; + struct tls_connection *conn; + + wpa_printf(MSG_DEBUG, "SSL: connection init"); + + conn = os_zalloc(sizeof(*conn)); + if (!conn) + return NULL; + conn->ssl = wolfSSL_new(ssl_ctx); + if (!conn->ssl) { + os_free(conn); + return NULL; + } + + wolfSSL_SetIOReadCtx(conn->ssl, &conn->input); + wolfSSL_SetIOWriteCtx(conn->ssl, &conn->output); + wolfSSL_set_ex_data(conn->ssl, 0, conn); + conn->context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0); + + /* Need randoms post-hanshake for EAP-FAST, export key and deriving + * session ID in EAP methods. */ + wolfSSL_KeepArrays(conn->ssl); + wolfSSL_KeepHandshakeResources(conn->ssl); + wolfSSL_UseClientSuites(conn->ssl); + + return conn; +} + + +void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn) +{ + if (!conn) + return; + + wpa_printf(MSG_DEBUG, "SSL: connection deinit"); + + /* parts */ + wolfSSL_free(conn->ssl); + os_free(conn->subject_match); + os_free(conn->alt_subject_match); + os_free(conn->suffix_match); + os_free(conn->domain_match); + + /* self */ + os_free(conn); +} + + +int tls_connection_established(void *tls_ctx, struct tls_connection *conn) +{ + return conn ? wolfSSL_is_init_finished(conn->ssl) : 0; +} + + +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn) +{ + /* TODO */ + return NULL; +} + + +int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) +{ + WOLFSSL_SESSION *session; + + if (!conn) + return -1; + + wpa_printf(MSG_DEBUG, "SSL: connection shutdown"); + + /* Set quiet as OpenSSL does */ + wolfSSL_set_quiet_shutdown(conn->ssl, 1); + wolfSSL_shutdown(conn->ssl); + + session = wolfSSL_get_session(conn->ssl); + if (wolfSSL_clear(conn->ssl) != 1) + return -1; + wolfSSL_set_session(conn->ssl, session); + + return 0; +} + + +static int tls_connection_set_subject_match(struct tls_connection *conn, + const char *subject_match, + const char *alt_subject_match, + const char *suffix_match, + const char *domain_match) +{ + os_free(conn->subject_match); + conn->subject_match = NULL; + if (subject_match) { + conn->subject_match = os_strdup(subject_match); + if (!conn->subject_match) + return -1; + } + + os_free(conn->alt_subject_match); + conn->alt_subject_match = NULL; + if (alt_subject_match) { + conn->alt_subject_match = os_strdup(alt_subject_match); + if (!conn->alt_subject_match) + return -1; + } + + os_free(conn->suffix_match); + conn->suffix_match = NULL; + if (suffix_match) { + conn->suffix_match = os_strdup(suffix_match); + if (!conn->suffix_match) + return -1; + } + + os_free(conn->domain_match); + conn->domain_match = NULL; + if (domain_match) { + conn->domain_match = os_strdup(domain_match); + if (!conn->domain_match) + return -1; + } + + return 0; +} + + +static int tls_connection_dh(struct tls_connection *conn, const char *dh_file, + const u8 *dh_blob, size_t blob_len) +{ + if (!dh_file && !dh_blob) + return 0; + + wolfSSL_set_accept_state(conn->ssl); + + if (dh_blob) { + if (wolfSSL_SetTmpDH_buffer(conn->ssl, dh_blob, blob_len, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, "SSL: use DH DER blob failed"); + return -1; + } + wpa_printf(MSG_DEBUG, "SSL: use DH blob OK"); + return 0; + } + + if (dh_file) { + wpa_printf(MSG_INFO, "SSL: use DH PEM file: %s", dh_file); + if (wolfSSL_SetTmpDH_file(conn->ssl, dh_file, + SSL_FILETYPE_PEM) < 0) { + wpa_printf(MSG_INFO, "SSL: use DH PEM file failed"); + if (wolfSSL_SetTmpDH_file(conn->ssl, dh_file, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, + "SSL: use DH DER file failed"); + return -1; + } + } + wpa_printf(MSG_DEBUG, "SSL: use DH file OK"); + return 0; + } + + return 0; +} + + +static int tls_connection_client_cert(struct tls_connection *conn, + const char *client_cert, + const u8 *client_cert_blob, + size_t blob_len) +{ + if (!client_cert && !client_cert_blob) + return 0; + + if (client_cert_blob) { + if (wolfSSL_use_certificate_chain_buffer_format( + conn->ssl, client_cert_blob, blob_len, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, + "SSL: use client cert DER blob failed"); + return -1; + } + wpa_printf(MSG_DEBUG, "SSL: use client cert blob OK"); + return 0; + } + + if (client_cert) { + if (wolfSSL_use_certificate_chain_file(conn->ssl, + client_cert) < 0) { + wpa_printf(MSG_INFO, + "SSL: use client cert PEM file failed"); + if (wolfSSL_use_certificate_chain_file_format( + conn->ssl, client_cert, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, + "SSL: use client cert DER file failed"); + return -1; + } + } + wpa_printf(MSG_DEBUG, "SSL: use client cert file OK"); + return 0; + } + + return 0; +} + + +static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) +{ + if (!password) + return 0; + os_strlcpy(buf, (char *) password, size); + return os_strlen(buf); +} + + +static int tls_connection_private_key(void *tls_ctx, + struct tls_connection *conn, + const char *private_key, + const char *private_key_passwd, + const u8 *private_key_blob, + size_t blob_len) +{ + WOLFSSL_CTX *ctx = tls_ctx; + char *passwd = NULL; + int ok = 0; + + if (!private_key && !private_key_blob) + return 0; + + if (private_key_passwd) { + passwd = os_strdup(private_key_passwd); + if (!passwd) + return -1; + } + + wolfSSL_CTX_set_default_passwd_cb(ctx, tls_passwd_cb); + wolfSSL_CTX_set_default_passwd_cb_userdata(ctx, passwd); + + if (private_key_blob) { + if (wolfSSL_use_PrivateKey_buffer(conn->ssl, + private_key_blob, blob_len, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, + "SSL: use private DER blob failed"); + } else { + wpa_printf(MSG_DEBUG, "SSL: use private key blob OK"); + ok = 1; + } + } + + if (!ok && private_key) { + if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key, + SSL_FILETYPE_PEM) < 0) { + wpa_printf(MSG_INFO, + "SSL: use private key PEM file failed"); + if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key, + SSL_FILETYPE_ASN1) < 0) + { + wpa_printf(MSG_INFO, + "SSL: use private key DER file failed"); + } else { + ok = 1; + } + } else { + ok = 1; + } + + if (ok) + wpa_printf(MSG_DEBUG, "SSL: use private key file OK"); + } + + wolfSSL_CTX_set_default_passwd_cb(ctx, NULL); + os_free(passwd); + + if (!ok) + return -1; + + return 0; +} + + +static int tls_match_alt_subject_component(WOLFSSL_X509 *cert, int type, + const char *value, size_t len) +{ + WOLFSSL_ASN1_OBJECT *gen; + void *ext; + int found = 0; + int i; + + ext = wolfSSL_X509_get_ext_d2i(cert, ALT_NAMES_OID, NULL, NULL); + + for (i = 0; ext && i < wolfSSL_sk_num(ext); i++) { + gen = wolfSSL_sk_value(ext, i); + if (gen->type != type) + continue; + if (os_strlen((char *) gen->obj) == len && + os_memcmp(value, gen->obj, len) == 0) + found++; + } + + wolfSSL_sk_ASN1_OBJECT_free(ext); + + return found; +} + + +static int tls_match_alt_subject(WOLFSSL_X509 *cert, const char *match) +{ + int type; + const char *pos, *end; + size_t len; + + pos = match; + do { + if (os_strncmp(pos, "EMAIL:", 6) == 0) { + type = GEN_EMAIL; + pos += 6; + } else if (os_strncmp(pos, "DNS:", 4) == 0) { + type = GEN_DNS; + pos += 4; + } else if (os_strncmp(pos, "URI:", 4) == 0) { + type = GEN_URI; + pos += 4; + } else { + wpa_printf(MSG_INFO, + "TLS: Invalid altSubjectName match '%s'", + pos); + return 0; + } + end = os_strchr(pos, ';'); + while (end) { + if (os_strncmp(end + 1, "EMAIL:", 6) == 0 || + os_strncmp(end + 1, "DNS:", 4) == 0 || + os_strncmp(end + 1, "URI:", 4) == 0) + break; + end = os_strchr(end + 1, ';'); + } + if (end) + len = end - pos; + else + len = os_strlen(pos); + if (tls_match_alt_subject_component(cert, type, pos, len) > 0) + return 1; + pos = end + 1; + } while (end); + + return 0; +} + + +static int domain_suffix_match(const char *val, size_t len, const char *match, + int full) +{ + size_t i, match_len; + + /* Check for embedded nuls that could mess up suffix matching */ + for (i = 0; i < len; i++) { + if (val[i] == '\0') { + wpa_printf(MSG_DEBUG, + "TLS: Embedded null in a string - reject"); + return 0; + } + } + + match_len = os_strlen(match); + if (match_len > len || (full && match_len != len)) + return 0; + + if (os_strncasecmp(val + len - match_len, match, match_len) != 0) + return 0; /* no match */ + + if (match_len == len) + return 1; /* exact match */ + + if (val[len - match_len - 1] == '.') + return 1; /* full label match completes suffix match */ + + wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match"); + return 0; +} + + +static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) +{ + WOLFSSL_ASN1_OBJECT *gen; + void *ext; + int i; + int j; + int dns_name = 0; + WOLFSSL_X509_NAME *name; + + wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s", + full ? "" : "suffix ", match); + + ext = wolfSSL_X509_get_ext_d2i(cert, ALT_NAMES_OID, NULL, NULL); + + for (j = 0; ext && j < wolfSSL_sk_num(ext); j++) { + gen = wolfSSL_sk_value(ext, j); + if (gen->type != ALT_NAMES_OID) + continue; + dns_name++; + wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName", + gen->obj, os_strlen((char *)gen->obj)); + if (domain_suffix_match((const char *) gen->obj, + os_strlen((char *) gen->obj), match, + full) == 1) { + wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found", + full ? "Match" : "Suffix match"); + wolfSSL_sk_ASN1_OBJECT_free(ext); + return 1; + } + } + wolfSSL_sk_ASN1_OBJECT_free(ext); + + if (dns_name) { + wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched"); + return 0; + } + + name = wolfSSL_X509_get_subject_name(cert); + i = -1; + for (;;) { + WOLFSSL_X509_NAME_ENTRY *e; + WOLFSSL_ASN1_STRING *cn; + + i = wolfSSL_X509_NAME_get_index_by_NID(name, ASN_COMMON_NAME, + i); + if (i == -1) + break; + e = wolfSSL_X509_NAME_get_entry(name, i); + if (!e) + continue; + cn = wolfSSL_X509_NAME_ENTRY_get_data(e); + if (!cn) + continue; + wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", + cn->data, cn->length); + if (domain_suffix_match(cn->data, cn->length, match, full) == 1) + { + wpa_printf(MSG_DEBUG, "TLS: %s in commonName found", + full ? "Match" : "Suffix match"); + return 1; + } + } + + wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found", + full ? "" : "suffix "); + return 0; +} + + +static enum tls_fail_reason wolfssl_tls_fail_reason(int err) +{ + switch (err) { + case X509_V_ERR_CERT_REVOKED: + return TLS_FAIL_REVOKED; + case ASN_BEFORE_DATE_E: + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_CRL_NOT_YET_VALID: + return TLS_FAIL_NOT_YET_VALID; + case ASN_AFTER_DATE_E: + case X509_V_ERR_CERT_HAS_EXPIRED: + case X509_V_ERR_CRL_HAS_EXPIRED: + return TLS_FAIL_EXPIRED; + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + case X509_V_ERR_UNABLE_TO_GET_CRL: + case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: + case X509_V_ERR_CERT_CHAIN_TOO_LONG: + case X509_V_ERR_PATH_LENGTH_EXCEEDED: + case X509_V_ERR_INVALID_CA: + return TLS_FAIL_UNTRUSTED; + case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: + case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: + case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: + case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: + case X509_V_ERR_CERT_UNTRUSTED: + case X509_V_ERR_CERT_REJECTED: + return TLS_FAIL_BAD_CERTIFICATE; + default: + return TLS_FAIL_UNSPECIFIED; + } +} + + +static const char * wolfssl_tls_err_string(int err, const char *err_str) +{ + switch (err) { + case ASN_BEFORE_DATE_E: + return "certificate is not yet valid"; + case ASN_AFTER_DATE_E: + return "certificate has expired"; + default: + return err_str; + } +} + + +static struct wpabuf * get_x509_cert(WOLFSSL_X509 *cert) +{ + struct wpabuf *buf = NULL; + const u8 *data; + int cert_len; + + data = wolfSSL_X509_get_der(cert, &cert_len); + if (!data) + buf = wpabuf_alloc_copy(data, cert_len); + + return buf; +} + + +static void wolfssl_tls_fail_event(struct tls_connection *conn, + WOLFSSL_X509 *err_cert, int err, int depth, + const char *subject, const char *err_str, + enum tls_fail_reason reason) +{ + union tls_event_data ev; + struct wpabuf *cert = NULL; + struct tls_context *context = conn->context; + + if (!context->event_cb) + return; + + cert = get_x509_cert(err_cert); + os_memset(&ev, 0, sizeof(ev)); + ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ? + reason : wolfssl_tls_fail_reason(err); + ev.cert_fail.depth = depth; + ev.cert_fail.subject = subject; + ev.cert_fail.reason_txt = wolfssl_tls_err_string(err, err_str); + ev.cert_fail.cert = cert; + context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); + wpabuf_free(cert); +} + + +static void wolfssl_tls_cert_event(struct tls_connection *conn, + WOLFSSL_X509 *err_cert, int depth, + const char *subject) +{ + struct wpabuf *cert = NULL; + union tls_event_data ev; + struct tls_context *context = conn->context; + char *alt_subject[TLS_MAX_ALT_SUBJECT]; + int alt, num_alt_subject = 0; + WOLFSSL_ASN1_OBJECT *gen; + void *ext; + int i; +#ifdef CONFIG_SHA256 + u8 hash[32]; +#endif /* CONFIG_SHA256 */ + + if (!context->event_cb) + return; + + os_memset(&ev, 0, sizeof(ev)); + if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) || + context->cert_in_cb) { + cert = get_x509_cert(err_cert); + ev.peer_cert.cert = cert; + } + +#ifdef CONFIG_SHA256 + if (cert) { + const u8 *addr[1]; + size_t len[1]; + + addr[0] = wpabuf_head(cert); + len[0] = wpabuf_len(cert); + if (sha256_vector(1, addr, len, hash) == 0) { + ev.peer_cert.hash = hash; + ev.peer_cert.hash_len = sizeof(hash); + } + } +#endif /* CONFIG_SHA256 */ + + ev.peer_cert.depth = depth; + ev.peer_cert.subject = subject; + + ext = wolfSSL_X509_get_ext_d2i(err_cert, ALT_NAMES_OID, NULL, NULL); + for (i = 0; ext && i < wolfSSL_sk_num(ext); i++) { + char *pos; + + if (num_alt_subject == TLS_MAX_ALT_SUBJECT) + break; + gen = wolfSSL_sk_value((void *) ext, i); + if (gen->type != GEN_EMAIL && + gen->type != GEN_DNS && + gen->type != GEN_URI) + continue; + + pos = os_malloc(10 + os_strlen((char *) gen->obj) + 1); + if (!pos) + break; + alt_subject[num_alt_subject++] = pos; + + switch (gen->type) { + case GEN_EMAIL: + os_memcpy(pos, "EMAIL:", 6); + pos += 6; + break; + case GEN_DNS: + os_memcpy(pos, "DNS:", 4); + pos += 4; + break; + case GEN_URI: + os_memcpy(pos, "URI:", 4); + pos += 4; + break; + } + + os_memcpy(pos, gen->obj, os_strlen((char *)gen->obj)); + pos += os_strlen((char *)gen->obj); + *pos = '\0'; + } + wolfSSL_sk_ASN1_OBJECT_free(ext); + + for (alt = 0; alt < num_alt_subject; alt++) + ev.peer_cert.altsubject[alt] = alt_subject[alt]; + ev.peer_cert.num_altsubject = num_alt_subject; + + context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev); + wpabuf_free(cert); + for (alt = 0; alt < num_alt_subject; alt++) + os_free(alt_subject[alt]); +} + + +static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx) +{ + char buf[256]; + WOLFSSL_X509 *err_cert; + int err, depth; + WOLFSSL *ssl; + struct tls_connection *conn; + struct tls_context *context; + char *match, *altmatch, *suffix_match, *domain_match; + const char *err_str; + + err_cert = wolfSSL_X509_STORE_CTX_get_current_cert(x509_ctx); + if (!err_cert) { + wpa_printf(MSG_DEBUG, "wolfSSL: No Cert"); + return 0; + } + + err = wolfSSL_X509_STORE_CTX_get_error(x509_ctx); + depth = wolfSSL_X509_STORE_CTX_get_error_depth(x509_ctx); + ssl = wolfSSL_X509_STORE_CTX_get_ex_data( + x509_ctx, wolfSSL_get_ex_data_X509_STORE_CTX_idx()); + wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_subject_name(err_cert), buf, + sizeof(buf)); + + conn = wolfSSL_get_ex_data(ssl, 0); + if (!conn) { + wpa_printf(MSG_DEBUG, "wolfSSL: No ex_data"); + return 0; + } + + if (depth == 0) + conn->peer_cert = err_cert; + else if (depth == 1) + conn->peer_issuer = err_cert; + else if (depth == 2) + conn->peer_issuer_issuer = err_cert; + + context = conn->context; + match = conn->subject_match; + altmatch = conn->alt_subject_match; + suffix_match = conn->suffix_match; + domain_match = conn->domain_match; + + if (!preverify_ok && !conn->ca_cert_verify) + preverify_ok = 1; + if (!preverify_ok && depth > 0 && conn->server_cert_only) + preverify_ok = 1; + if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) && + (err == X509_V_ERR_CERT_HAS_EXPIRED || + err == ASN_AFTER_DATE_E || err == ASN_BEFORE_DATE_E || + err == X509_V_ERR_CERT_NOT_YET_VALID)) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Ignore certificate validity time mismatch"); + preverify_ok = 1; + } + + err_str = wolfSSL_X509_verify_cert_error_string(err); + +#ifdef CONFIG_SHA256 + /* + * Do not require preverify_ok so we can explicity allow otherwise + * invalid pinned server certificates. + */ + if (depth == 0 && conn->server_cert_only) { + struct wpabuf *cert; + + cert = get_x509_cert(err_cert); + if (!cert) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Could not fetch server certificate data"); + preverify_ok = 0; + } else { + u8 hash[32]; + const u8 *addr[1]; + size_t len[1]; + + addr[0] = wpabuf_head(cert); + len[0] = wpabuf_len(cert); + if (sha256_vector(1, addr, len, hash) < 0 || + os_memcmp(conn->srv_cert_hash, hash, 32) != 0) { + err_str = "Server certificate mismatch"; + err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN; + preverify_ok = 0; + } else if (!preverify_ok) { + /* + * Certificate matches pinned certificate, allow + * regardless of other problems. + */ + wpa_printf(MSG_DEBUG, + "wolfSSL: Ignore validation issues for a pinned server certificate"); + preverify_ok = 1; + } + wpabuf_free(cert); + } + } +#endif /* CONFIG_SHA256 */ + + if (!preverify_ok) { + wpa_printf(MSG_WARNING, + "TLS: Certificate verification failed, error %d (%s) depth %d for '%s'", + err, err_str, depth, buf); + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + err_str, TLS_FAIL_UNSPECIFIED); + return preverify_ok; + } + + wpa_printf(MSG_DEBUG, + "TLS: %s - preverify_ok=%d err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'", + __func__, preverify_ok, err, err_str, + conn->ca_cert_verify, depth, buf); + if (depth == 0 && match && os_strstr(buf, match) == NULL) { + wpa_printf(MSG_WARNING, + "TLS: Subject '%s' did not match with '%s'", + buf, match); + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Subject mismatch", + TLS_FAIL_SUBJECT_MISMATCH); + } else if (depth == 0 && altmatch && + !tls_match_alt_subject(err_cert, altmatch)) { + wpa_printf(MSG_WARNING, + "TLS: altSubjectName match '%s' not found", + altmatch); + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "AltSubject mismatch", + TLS_FAIL_ALTSUBJECT_MISMATCH); + } else if (depth == 0 && suffix_match && + !tls_match_suffix(err_cert, suffix_match, 0)) { + wpa_printf(MSG_WARNING, + "TLS: Domain suffix match '%s' not found", + suffix_match); + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Domain suffix mismatch", + TLS_FAIL_DOMAIN_SUFFIX_MISMATCH); + } else if (depth == 0 && domain_match && + !tls_match_suffix(err_cert, domain_match, 1)) { + wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found", + domain_match); + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Domain mismatch", + TLS_FAIL_DOMAIN_MISMATCH); + } else { + wolfssl_tls_cert_event(conn, err_cert, depth, buf); + } + + if (conn->cert_probe && preverify_ok && depth == 0) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Reject server certificate on probe-only run"); + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Server certificate chain probe", + TLS_FAIL_SERVER_CHAIN_PROBE); + } + +#ifdef HAVE_OCSP_WOLFSSL + if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) && + preverify_ok) { + enum ocsp_result res; + + res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert, + conn->peer_issuer, + conn->peer_issuer_issuer); + if (res == OCSP_REVOKED) { + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "certificate revoked", + TLS_FAIL_REVOKED); + if (err == X509_V_OK) + X509_STORE_CTX_set_error( + x509_ctx, X509_V_ERR_CERT_REVOKED); + } else if (res != OCSP_GOOD && + (conn->flags & TLS_CONN_REQUIRE_OCSP)) { + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "bad certificate status response", + TLS_FAIL_UNSPECIFIED); + } + } +#endif /* HAVE_OCSP_WOLFSSL */ + if (depth == 0 && preverify_ok && context->event_cb != NULL) + context->event_cb(context->cb_ctx, + TLS_CERT_CHAIN_SUCCESS, NULL); + + return preverify_ok; +} + + +static int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn, + const char *ca_cert, + const u8 *ca_cert_blob, size_t blob_len, + const char *ca_path) +{ + WOLFSSL_CTX *ctx = tls_ctx; + + wolfSSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + conn->ca_cert_verify = 1; + + if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Probe for server certificate chain"); + conn->cert_probe = 1; + conn->ca_cert_verify = 0; + return 0; + } + + if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) { +#ifdef CONFIG_SHA256 + const char *pos = ca_cert + 7; + + if (os_strncmp(pos, "server/sha256/", 14) != 0) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Unsupported ca_cert hash value '%s'", + ca_cert); + return -1; + } + pos += 14; + if (os_strlen(pos) != 32 * 2) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Unexpected SHA256 hash length in ca_cert '%s'", + ca_cert); + return -1; + } + if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Invalid SHA256 hash value in ca_cert '%s'", + ca_cert); + return -1; + } + conn->server_cert_only = 1; + wpa_printf(MSG_DEBUG, + "wolfSSL: Checking only server certificate match"); + return 0; +#else /* CONFIG_SHA256 */ + wpa_printf(MSG_INFO, + "No SHA256 included in the build - cannot validate server certificate hash"); + return -1; +#endif /* CONFIG_SHA256 */ + } + + if (ca_cert_blob) { + if (wolfSSL_CTX_load_verify_buffer(ctx, ca_cert_blob, blob_len, + SSL_FILETYPE_ASN1) != + SSL_SUCCESS) { + wpa_printf(MSG_INFO, "SSL: failed to load CA blob"); + return -1; + } + wpa_printf(MSG_DEBUG, "SSL: use CA cert blob OK"); + return 0; + } + + if (ca_cert || ca_path) { + WOLFSSL_X509_STORE *cm = wolfSSL_X509_STORE_new(); + + if (!cm) { + wpa_printf(MSG_INFO, + "SSL: failed to create certificate store"); + return -1; + } + wolfSSL_CTX_set_cert_store(ctx, cm); + + if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, ca_path) != + SSL_SUCCESS) { + wpa_printf(MSG_INFO, + "SSL: failed to load ca_cert as PEM"); + + if (!ca_cert) + return -1; + + if (wolfSSL_CTX_der_load_verify_locations( + ctx, ca_cert, SSL_FILETYPE_ASN1) != + SSL_SUCCESS) { + wpa_printf(MSG_INFO, + "SSL: failed to load ca_cert as DER"); + return -1; + } + } + return 0; + } + + conn->ca_cert_verify = 0; + return 0; +} + + +static void tls_set_conn_flags(WOLFSSL *ssl, unsigned int flags) +{ +#ifdef HAVE_SESSION_TICKET +#if 0 + if (!(flags & TLS_CONN_DISABLE_SESSION_TICKET)) + wolfSSL_UseSessionTicket(ssl); +#endif +#endif /* HAVE_SESSION_TICKET */ + + if (flags & TLS_CONN_DISABLE_TLSv1_0) + wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1); + if (flags & TLS_CONN_DISABLE_TLSv1_1) + wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_1); + if (flags & TLS_CONN_DISABLE_TLSv1_2) + wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_2); +} + + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params) +{ + wpa_printf(MSG_DEBUG, "SSL: set params"); + + if (tls_connection_set_subject_match(conn, params->subject_match, + params->altsubject_match, + params->suffix_match, + params->domain_match) < 0) { + wpa_printf(MSG_INFO, "Error setting subject match"); + return -1; + } + + if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert, + params->ca_cert_blob, + params->ca_cert_blob_len, + params->ca_path) < 0) { + wpa_printf(MSG_INFO, "Error setting CA cert"); + return -1; + } + + if (tls_connection_client_cert(conn, params->client_cert, + params->client_cert_blob, + params->client_cert_blob_len) < 0) { + wpa_printf(MSG_INFO, "Error setting client cert"); + return -1; + } + + if (tls_connection_private_key(tls_ctx, conn, params->private_key, + params->private_key_passwd, + params->private_key_blob, + params->private_key_blob_len) < 0) { + wpa_printf(MSG_INFO, "Error setting private key"); + return -1; + } + + if (tls_connection_dh(conn, params->dh_file, params->dh_blob, + params->dh_blob_len) < 0) { + wpa_printf(MSG_INFO, "Error setting DH"); + return -1; + } + + if (params->openssl_ciphers && + wolfSSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "wolfSSL: Failed to set cipher string '%s'", + params->openssl_ciphers); + return -1; + } + + tls_set_conn_flags(conn->ssl, params->flags); + +#ifdef HAVE_CERTIFICATE_STATUS_REQUEST + if (params->flags & TLS_CONN_REQUEST_OCSP) { + if (wolfSSL_UseOCSPStapling(conn->ssl, WOLFSSL_CSR_OCSP, + WOLFSSL_CSR_OCSP_USE_NONCE) != + SSL_SUCCESS) + return -1; + wolfSSL_CTX_EnableOCSP(tls_ctx, 0); + } +#endif /* HAVE_CERTIFICATE_STATUS_REQUEST */ +#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2 + if (params->flags & TLS_CONN_REQUEST_OCSP) { + if (wolfSSL_UseOCSPStaplingV2(conn->ssl, + WOLFSSL_CSR2_OCSP_MULTI, 0) != + SSL_SUCCESS) + return -1; + wolfSSL_CTX_EnableOCSP(tls_ctx, 0); + } +#endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */ +#if !defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \ + !defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) +#ifdef HAVE_OCSP + if (params->flags & TLS_CONN_REQUEST_OCSP) + wolfSSL_CTX_EnableOCSP(ctx, 0); +#else /* HAVE_OCSP */ + if (params->flags & TLS_CONN_REQUIRE_OCSP) { + wpa_printf(MSG_INFO, + "wolfSSL: No OCSP support included - reject configuration"); + return -1; + } + if (params->flags & TLS_CONN_REQUEST_OCSP) { + wpa_printf(MSG_DEBUG, + "wolfSSL: No OCSP support included - allow optional OCSP case to continue"); + } +#endif /* HAVE_OCSP */ +#endif /* !HAVE_CERTIFICATE_STATUS_REQUEST && + * !HAVE_CERTIFICATE_STATUS_REQUEST_V2 */ + + conn->flags = params->flags; + + return 0; +} + + +static int tls_global_ca_cert(void *ssl_ctx, const char *ca_cert) +{ + WOLFSSL_CTX *ctx = ssl_ctx; + + if (ca_cert) { + if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, NULL) != 1) + { + wpa_printf(MSG_WARNING, + "Failed to load root certificates"); + return -1; + } + + wpa_printf(MSG_DEBUG, + "TLS: Trusted root certificate(s) loaded"); + } + + return 0; +} + + +static int tls_global_client_cert(void *ssl_ctx, const char *client_cert) +{ + WOLFSSL_CTX *ctx = ssl_ctx; + + if (!client_cert) + return 0; + + if (wolfSSL_CTX_use_certificate_chain_file_format(ctx, client_cert, + SSL_FILETYPE_ASN1) != + SSL_SUCCESS && + wolfSSL_CTX_use_certificate_chain_file(ctx, client_cert) != + SSL_SUCCESS) { + wpa_printf(MSG_INFO, "Failed to load client certificate"); + return -1; + } + + wpa_printf(MSG_DEBUG, "SSL: Loaded global client certificate: %s", + client_cert); + + return 0; +} + + +static int tls_global_private_key(void *ssl_ctx, const char *private_key, + const char *private_key_passwd) +{ + WOLFSSL_CTX *ctx = ssl_ctx; + char *passwd = NULL; + int ret = 0; + + if (!private_key) + return 0; + + if (private_key_passwd) { + passwd = os_strdup(private_key_passwd); + if (!passwd) + return -1; + } + + wolfSSL_CTX_set_default_passwd_cb(ctx, tls_passwd_cb); + wolfSSL_CTX_set_default_passwd_cb_userdata(ctx, passwd); + + if (wolfSSL_CTX_use_PrivateKey_file(ctx, private_key, + SSL_FILETYPE_ASN1) != 1 && + wolfSSL_CTX_use_PrivateKey_file(ctx, private_key, + SSL_FILETYPE_PEM) != 1) { + wpa_printf(MSG_INFO, "Failed to load private key"); + ret = -1; + } + + wpa_printf(MSG_DEBUG, "SSL: Loaded global private key"); + + os_free(passwd); + wolfSSL_CTX_set_default_passwd_cb(ctx, NULL); + + return ret; +} + + +static int tls_global_dh(void *ssl_ctx, const char *dh_file, + const u8 *dh_blob, size_t blob_len) +{ + WOLFSSL_CTX *ctx = ssl_ctx; + + if (!dh_file && !dh_blob) + return 0; + + if (dh_blob) { + if (wolfSSL_CTX_SetTmpDH_buffer(ctx, dh_blob, blob_len, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, + "SSL: global use DH DER blob failed"); + return -1; + } + wpa_printf(MSG_DEBUG, "SSL: global use DH blob OK"); + return 0; + } + + if (dh_file) { + if (wolfSSL_CTX_SetTmpDH_file(ctx, dh_file, SSL_FILETYPE_PEM) < + 0) { + wpa_printf(MSG_INFO, + "SSL: global use DH PEM file failed"); + if (wolfSSL_CTX_SetTmpDH_file(ctx, dh_file, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, + "SSL: global use DH DER file failed"); + return -1; + } + } + wpa_printf(MSG_DEBUG, "SSL: global use DH file OK"); + return 0; + } + + return 0; +} + + +#ifdef HAVE_OCSP + +int ocsp_status_cb(void *unused, const char *url, int url_sz, + unsigned char *request, int request_sz, + unsigned char **response) +{ + size_t len; + + (void) unused; + + if (!url) { + wpa_printf(MSG_DEBUG, + "wolfSSL: OCSP status callback - no response configured"); + *response = NULL; + return 0; + } + + *response = (unsigned char *) os_readfile(url, &len); + if (!*response) { + wpa_printf(MSG_DEBUG, + "wolfSSL: OCSP status callback - could not read response file"); + return -1; + } + wpa_printf(MSG_DEBUG, + "wolfSSL: OCSP status callback - send cached response"); + return len; +} + + +void ocsp_resp_free_cb(void *ocsp_stapling_response, unsigned char *response) +{ + os_free(response); +} + +#endif /* HAVE_OCSP */ + + +int tls_global_set_params(void *tls_ctx, + const struct tls_connection_params *params) +{ + wpa_printf(MSG_DEBUG, "SSL: global set params"); + + if (tls_global_ca_cert(tls_ctx, params->ca_cert) < 0) { + wpa_printf(MSG_INFO, "SSL: Failed to load ca cert file '%s'", + params->ca_cert); + return -1; + } + + if (tls_global_client_cert(tls_ctx, params->client_cert) < 0) { + wpa_printf(MSG_INFO, + "SSL: Failed to load client cert file '%s'", + params->client_cert); + return -1; + } + + if (tls_global_private_key(tls_ctx, params->private_key, + params->private_key_passwd) < 0) { + wpa_printf(MSG_INFO, + "SSL: Failed to load private key file '%s'", + params->private_key); + return -1; + } + + if (tls_global_dh(tls_ctx, params->dh_file, params->dh_blob, + params->dh_blob_len) < 0) { + wpa_printf(MSG_INFO, "SSL: Failed to load DH file '%s'", + params->dh_file); + return -1; + } + + if (params->openssl_ciphers && + wolfSSL_CTX_set_cipher_list(tls_ctx, + params->openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "wolfSSL: Failed to set cipher string '%s'", + params->openssl_ciphers); + return -1; + } + +#ifdef HAVE_SESSION_TICKET + /* Session ticket is off by default - can't disable once on. */ + if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET)) + wolfSSL_CTX_UseSessionTicket(tls_ctx); +#endif /* HAVE_SESSION_TICKET */ + +#ifdef HAVE_OCSP + if (params->ocsp_stapling_response) { + wolfSSL_CTX_SetOCSP_OverrideURL(tls_ctx, + params->ocsp_stapling_response); + wolfSSL_CTX_SetOCSP_Cb(tls_ctx, ocsp_status_cb, + ocsp_resp_free_cb, NULL); + } +#endif /* HAVE_OCSP */ + + return 0; +} + + +int tls_global_set_verify(void *tls_ctx, int check_crl) +{ + wpa_printf(MSG_DEBUG, "SSL: global set verify: %d", check_crl); + + if (check_crl) { + /* Hack to Enable CRLs. */ + wolfSSL_CTX_LoadCRLBuffer(tls_ctx, NULL, 0, SSL_FILETYPE_PEM); + } + + return 0; +} + + +int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, + int verify_peer, unsigned int flags, + const u8 *session_ctx, size_t session_ctx_len) +{ + if (!conn) + return -1; + + wpa_printf(MSG_DEBUG, "SSL: set verify: %d", verify_peer); + + if (verify_peer) { + conn->ca_cert_verify = 1; + wolfSSL_set_verify(conn->ssl, SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + tls_verify_cb); + } else { + conn->ca_cert_verify = 0; + wolfSSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); + } + + wolfSSL_set_accept_state(conn->ssl); + + /* TODO: do we need to fake a session like OpenSSL does here? */ + + return 0; +} + + +static struct wpabuf * wolfssl_handshake(struct tls_connection *conn, + const struct wpabuf *in_data, + int server) +{ + int res; + + wolfssl_reset_out_data(&conn->output); + + /* Initiate TLS handshake or continue the existing handshake */ + if (server) { + wolfSSL_set_accept_state(conn->ssl); + res = wolfSSL_accept(conn->ssl); + wpa_printf(MSG_DEBUG, "SSL: wolfSSL_accept: %d", res); + } else { + wolfSSL_set_connect_state(conn->ssl); + res = wolfSSL_connect(conn->ssl); + wpa_printf(MSG_DEBUG, "SSL: wolfSSL_connect: %d", res); + } + + if (res != 1) { + int err = wolfSSL_get_error(conn->ssl, res); + + if (err == SSL_ERROR_WANT_READ) { + wpa_printf(MSG_DEBUG, + "SSL: wolfSSL_connect - want more data"); + } else if (err == SSL_ERROR_WANT_WRITE) { + wpa_printf(MSG_DEBUG, + "SSL: wolfSSL_connect - want to write"); + } else { + char msg[80]; + + wpa_printf(MSG_DEBUG, + "SSL: wolfSSL_connect - failed %s", + wolfSSL_ERR_error_string(err, msg)); + conn->failed++; + } + } + + return conn->output.out_data; +} + + +static struct wpabuf * wolfssl_get_appl_data(struct tls_connection *conn, + size_t max_len) +{ + int res; + struct wpabuf *appl_data = wpabuf_alloc(max_len + 100); + + if (!appl_data) + return NULL; + + res = wolfSSL_read(conn->ssl, wpabuf_mhead(appl_data), + wpabuf_size(appl_data)); + if (res < 0) { + int err = wolfSSL_get_error(conn->ssl, res); + + if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { + wpa_printf(MSG_DEBUG, + "SSL: No Application Data included"); + } else { + char msg[80]; + + wpa_printf(MSG_DEBUG, + "Failed to read possible Application Data %s", + wolfSSL_ERR_error_string(err, msg)); + } + + wpabuf_free(appl_data); + return NULL; + } + + wpabuf_put(appl_data, res); + wpa_hexdump_buf_key(MSG_MSGDUMP, + "SSL: Application Data in Finished message", + appl_data); + return appl_data; +} + + +static struct wpabuf * +wolfssl_connection_handshake(struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data, int server) +{ + struct wpabuf *out_data; + + wolfssl_reset_in_data(&conn->input, in_data); + + if (appl_data) + *appl_data = NULL; + + out_data = wolfssl_handshake(conn, in_data, server); + if (!out_data) + return NULL; + + if (wolfSSL_is_init_finished(conn->ssl)) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Handshake finished - resumed=%d", + tls_connection_resumed(NULL, conn)); + if (appl_data && in_data) + *appl_data = wolfssl_get_appl_data(conn, + wpabuf_len(in_data)); + } + + return out_data; +} + + +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + return wolfssl_connection_handshake(conn, in_data, appl_data, 0); +} + + +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + return wolfssl_connection_handshake(conn, in_data, appl_data, 1); +} + + +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + int res; + + if (!conn) + return NULL; + + wpa_printf(MSG_DEBUG, "SSL: encrypt: %ld bytes", wpabuf_len(in_data)); + + wolfssl_reset_out_data(&conn->output); + + res = wolfSSL_write(conn->ssl, wpabuf_head(in_data), + wpabuf_len(in_data)); + if (res < 0) { + int err = wolfSSL_get_error(conn->ssl, res); + char msg[80]; + + wpa_printf(MSG_INFO, "Encryption failed - SSL_write: %s", + wolfSSL_ERR_error_string(err, msg)); + return NULL; + } + + return conn->output.out_data; +} + + +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + int res; + struct wpabuf *buf; + + if (!conn) + return NULL; + + wpa_printf(MSG_DEBUG, "SSL: decrypt"); + + wolfssl_reset_in_data(&conn->input, in_data); + + /* Read decrypted data for further processing */ + /* + * Even though we try to disable TLS compression, it is possible that + * this cannot be done with all TLS libraries. Add extra buffer space + * to handle the possibility of the decrypted data being longer than + * input data. + */ + buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); + if (!buf) + return NULL; + res = wolfSSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf)); + if (res < 0) { + wpa_printf(MSG_INFO, "Decryption failed - SSL_read"); + wpabuf_free(buf); + return NULL; + } + wpabuf_put(buf, res); + + wpa_printf(MSG_DEBUG, "SSL: decrypt: %ld bytes", wpabuf_len(buf)); + + return buf; +} + + +int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn) +{ + return conn ? wolfSSL_session_reused(conn->ssl) : 0; +} + + +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, + u8 *ciphers) +{ + char buf[128], *pos, *end; + u8 *c; + int ret; + + if (!conn || !conn->ssl || !ciphers) + return -1; + + buf[0] = '\0'; + pos = buf; + end = pos + sizeof(buf); + + c = ciphers; + while (*c != TLS_CIPHER_NONE) { + const char *suite; + + switch (*c) { + case TLS_CIPHER_RC4_SHA: + suite = "RC4-SHA"; + break; + case TLS_CIPHER_AES128_SHA: + suite = "AES128-SHA"; + break; + case TLS_CIPHER_RSA_DHE_AES128_SHA: + suite = "DHE-RSA-AES128-SHA"; + break; + case TLS_CIPHER_ANON_DH_AES128_SHA: + suite = "ADH-AES128-SHA"; + break; + case TLS_CIPHER_RSA_DHE_AES256_SHA: + suite = "DHE-RSA-AES256-SHA"; + break; + case TLS_CIPHER_AES256_SHA: + suite = "AES256-SHA"; + break; + default: + wpa_printf(MSG_DEBUG, + "TLS: Unsupported cipher selection: %d", *c); + return -1; + } + ret = os_snprintf(pos, end - pos, ":%s", suite); + if (os_snprintf_error(end - pos, ret)) + break; + pos += ret; + + c++; + } + + wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", buf + 1); + + if (wolfSSL_set_cipher_list(conn->ssl, buf + 1) != 1) { + wpa_printf(MSG_DEBUG, "Cipher suite configuration failed"); + return -1; + } + + return 0; +} + + +int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + WOLFSSL_CIPHER *cipher; + const char *name; + + if (!conn || !conn->ssl) + return -1; + + cipher = wolfSSL_get_current_cipher(conn->ssl); + if (!cipher) + return -1; + + name = wolfSSL_CIPHER_get_name(cipher); + if (!name) + return -1; + + if (os_strcmp(name, "SSL_RSA_WITH_RC4_128_SHA") == 0) + os_strlcpy(buf, "RC4-SHA", buflen); + else if (os_strcmp(name, "TLS_RSA_WITH_AES_128_CBC_SHA") == 0) + os_strlcpy(buf, "AES128-SHA", buflen); + else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA") == 0) + os_strlcpy(buf, "DHE-RSA-AES128-SHA", buflen); + else if (os_strcmp(name, "TLS_DH_anon_WITH_AES_128_CBC_SHA") == 0) + os_strlcpy(buf, "ADH-AES128-SHA", buflen); + else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA") == 0) + os_strlcpy(buf, "DHE-RSA-AES256-SHA", buflen); + else if (os_strcmp(name, "TLS_RSA_WITH_AES_256_CBC_SHA") == 0) + os_strlcpy(buf, "AES256-SHA", buflen); + else + os_strlcpy(buf, name, buflen); + + return 0; +} + + +int tls_connection_enable_workaround(void *tls_ctx, + struct tls_connection *conn) +{ + /* no empty fragments in wolfSSL for now */ + return 0; +} + + +int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) +{ + if (!conn) + return -1; + + return conn->failed; +} + + +int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) +{ + if (!conn) + return -1; + + /* TODO: this is not incremented anywhere */ + return conn->read_alerts; +} + + +int tls_connection_get_write_alerts(void *tls_ctx, + struct tls_connection *conn) +{ + if (!conn) + return -1; + + /* TODO: this is not incremented anywhere */ + return conn->write_alerts; +} + + + +int tls_get_library_version(char *buf, size_t buf_len) +{ + return os_snprintf(buf, buf_len, "wolfSSL build=%s run=%s", + WOLFSSL_VERSION, wolfSSL_lib_version()); +} + +int tls_get_version(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + const char *name; + + if (!conn || !conn->ssl) + return -1; + + name = wolfSSL_get_version(conn->ssl); + if (!name) + return -1; + + os_strlcpy(buf, name, buflen); + return 0; +} + + +int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, + struct tls_random *keys) +{ + WOLFSSL *ssl; + + if (!conn || !keys) + return -1; + ssl = conn->ssl; + if (!ssl) + return -1; + + os_memset(keys, 0, sizeof(*keys)); + keys->client_random = conn->client_random; + keys->client_random_len = wolfSSL_get_client_random( + ssl, conn->client_random, sizeof(conn->client_random)); + keys->server_random = conn->server_random; + keys->server_random_len = wolfSSL_get_server_random( + ssl, conn->server_random, sizeof(conn->server_random)); + + return 0; +} + + +int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, + const char *label, u8 *out, size_t out_len) +{ + if (!conn || wolfSSL_make_eap_keys(conn->ssl, out, out_len, label) != 0) + return -1; + return 0; +} + + +#define SEED_LEN (RAN_LEN + RAN_LEN) + +int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, + u8 *out, size_t out_len) +{ + byte seed[SEED_LEN]; + int ret = -1; + WOLFSSL *ssl; + byte *tmp_out; + byte *_out; + int skip = 0; + byte *master_key; + unsigned int master_key_len; + byte *server_random; + unsigned int server_len; + byte *client_random; + unsigned int client_len; + + if (!conn || !conn->ssl) + return -1; + ssl = conn->ssl; + + skip = 2 * (wolfSSL_GetKeySize(ssl) + wolfSSL_GetHmacSize(ssl) + + wolfSSL_GetIVSize(ssl)); + + tmp_out = os_malloc(skip + out_len); + if (!tmp_out) + return -1; + _out = tmp_out; + + wolfSSL_get_keys(ssl, &master_key, &master_key_len, &server_random, + &server_len, &client_random, &client_len); + os_memcpy(seed, server_random, RAN_LEN); + os_memcpy(seed + RAN_LEN, client_random, RAN_LEN); + + if (wolfSSL_GetVersion(ssl) == WOLFSSL_TLSV1_2) { + tls_prf_sha256(master_key, master_key_len, + "key expansion", seed, sizeof(seed), + _out, skip + out_len); + ret = 0; + } else { + ret = tls_prf_sha1_md5(master_key, master_key_len, + "key expansion", seed, sizeof(seed), + _out, skip + out_len); + } + + os_memset(master_key, 0, master_key_len); + if (ret == 0) + os_memcpy(out, _out + skip, out_len); + bin_clear_free(tmp_out, skip + out_len); + + return ret; +} + + +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) + +int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len) +{ + (void) ssl_ctx; + + if (!conn || !conn->ssl || ext_type != 35) + return -1; + + if (wolfSSL_set_SessionTicket(conn->ssl, data, + (unsigned int) data_len) != 1) + return -1; + + return 0; +} + + +static int tls_sess_sec_cb(WOLFSSL *s, void *secret, int *secret_len, void *arg) +{ + struct tls_connection *conn = arg; + int ret; + unsigned char client_random[RAN_LEN]; + unsigned char server_random[RAN_LEN]; + word32 ticket_len = sizeof(conn->session_ticket); + + if (!conn || !conn->session_ticket_cb) + return 1; + + if (wolfSSL_get_client_random(s, client_random, + sizeof(client_random)) == 0 || + wolfSSL_get_server_random(s, server_random, + sizeof(server_random)) == 0 || + wolfSSL_get_SessionTicket(s, conn->session_ticket, + &ticket_len) != 1) + return 1; + + if (ticket_len == 0) + return 0; + + ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx, + conn->session_ticket, ticket_len, + client_random, server_random, secret); + if (ret <= 0) + return 1; + + *secret_len = SECRET_LEN; + return 0; +} + +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ + + +int tls_connection_set_session_ticket_cb(void *tls_ctx, + struct tls_connection *conn, + tls_session_ticket_cb cb, + void *ctx) +{ +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) + conn->session_ticket_cb = cb; + conn->session_ticket_cb_ctx = ctx; + + if (cb) { + if (wolfSSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb, + conn) != 1) + return -1; + } else { + if (wolfSSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1) + return -1; + } + + return 0; +#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ + return -1; +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ +} + + +void tls_connection_set_success_data_resumed(struct tls_connection *conn) +{ + wpa_printf(MSG_DEBUG, + "wolfSSL: Success data accepted for resumed session"); +} + + +void tls_connection_remove_session(struct tls_connection *conn) +{ + WOLFSSL_SESSION *sess; + + sess = wolfSSL_get_session(conn->ssl); + if (!sess) + return; + + wolfSSL_SSL_SESSION_set_timeout(sess, 0); + wpa_printf(MSG_DEBUG, + "wolfSSL: Removed cached session to disable session resumption"); +} + + +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) +{ + WOLFSSL_SESSION *sess; + struct wpabuf *old; + + wpa_printf(MSG_DEBUG, "wolfSSL: Set success data"); + + sess = wolfSSL_get_session(conn->ssl); + if (!sess) { + wpa_printf(MSG_DEBUG, + "wolfSSL: No session found for success data"); + goto fail; + } + + old = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + if (old) { + wpa_printf(MSG_DEBUG, "wolfSSL: Replacing old success data %p", + old); + wpabuf_free(old); + } + if (wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1) + goto fail; + + wpa_printf(MSG_DEBUG, "wolfSSL: Stored success data %p", data); + conn->success_data = 1; + return; + +fail: + wpa_printf(MSG_INFO, "wolfSSL: Failed to store success data"); + wpabuf_free(data); +} + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + WOLFSSL_SESSION *sess; + + wpa_printf(MSG_DEBUG, "wolfSSL: Get success data"); + + sess = wolfSSL_get_session(conn->ssl); + if (!sess) + return NULL; + return wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session); +} diff --git a/src/drivers/driver.h b/src/drivers/driver.h index a449cc934735..4ac9f16a0efc 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1,6 +1,6 @@ /* * Driver interface definition - * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -21,6 +21,10 @@ #include "common/defs.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_common.h" +#ifdef CONFIG_MACSEC +#include "pae/ieee802_1x_kay.h" +#endif /* CONFIG_MACSEC */ #include "utils/list.h" #define HOSTAPD_CHAN_DISABLED 0x00000001 @@ -61,6 +65,10 @@ /* Filter unicast IP packets encrypted using the GTK */ #define WPA_DATA_FRAME_FILTER_FLAG_GTK BIT(2) +#define HOSTAPD_DFS_REGION_FCC 1 +#define HOSTAPD_DFS_REGION_ETSI 2 +#define HOSTAPD_DFS_REGION_JP 3 + /** * enum reg_change_initiator - Regulatory change initiator */ @@ -133,6 +141,29 @@ struct hostapd_channel_data { unsigned int dfs_cac_ms; }; +#define HE_MAX_NUM_SS 8 +#define HE_MAX_PHY_CAPAB_SIZE 3 + +/** + * struct he_ppe_threshold - IEEE 802.11ax HE PPE Threshold + */ +struct he_ppe_threshold { + u32 numss_m1; + u32 ru_count; + u32 ppet16_ppet8_ru3_ru0[HE_MAX_NUM_SS]; +}; + +/** + * struct he_capabilities - IEEE 802.11ax HE capabilities + */ +struct he_capabilities { + u8 he_supported; + u32 phy_cap[HE_MAX_PHY_CAPAB_SIZE]; + u32 mac_cap; + u32 mcs; + struct he_ppe_threshold ppet; +}; + #define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0) #define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1) @@ -191,6 +222,11 @@ struct hostapd_hw_modes { u8 vht_mcs_set[8]; unsigned int flags; /* HOSTAPD_MODE_FLAG_* */ + + /** + * he_capab - HE (IEEE 802.11ax) capabilities + */ + struct he_capabilities he_capab; }; @@ -233,6 +269,9 @@ struct hostapd_hw_modes { * @est_throughput: Estimated throughput in kbps (this is calculated during * scan result processing if left zero by the driver wrapper) * @snr: Signal-to-noise ratio in dB (calculated during scan result processing) + * @parent_tsf: Time when the Beacon/Probe Response frame was received in terms + * of TSF of the BSS specified by %tsf_bssid. + * @tsf_bssid: The BSS that %parent_tsf TSF time refers to. * @ie_len: length of the following IE field in octets * @beacon_ie_len: length of the following Beacon IE field in octets * @@ -263,6 +302,8 @@ struct wpa_scan_res { unsigned int age; unsigned int est_throughput; int snr; + u64 parent_tsf; + u8 tsf_bssid[ETH_ALEN]; size_t ie_len; size_t beacon_ie_len; /* Followed by ie_len + beacon_ie_len octets of IE data */ @@ -447,6 +488,15 @@ struct wpa_driver_scan_params { unsigned int sched_scan_plans_num; /** + * sched_scan_start_delay - Delay to use before starting the first scan + * + * Delay (in seconds) before scheduling first scan plan cycle. The + * driver may ignore this parameter and start immediately (or at any + * other time), if this feature is not supported. + */ + u32 sched_scan_start_delay; + + /** * bssid - Specific BSSID to scan for * * This optional parameter can be used to replace the default wildcard @@ -455,6 +505,80 @@ struct wpa_driver_scan_params { */ const u8 *bssid; + /** + * scan_cookie - Unique identification representing the scan request + * + * This scan_cookie carries a unique identification representing the + * scan request if the host driver/kernel supports concurrent scan + * requests. This cookie is returned from the corresponding driver + * interface. + * + * Note: Unlike other parameters in this structure, scan_cookie is used + * only to return information instead of setting parameters for the + * scan. + */ + u64 scan_cookie; + + /** + * duration - Dwell time on each channel + * + * This optional parameter can be used to set the dwell time on each + * channel. In TUs. + */ + u16 duration; + + /** + * duration_mandatory - Whether the specified duration is mandatory + * + * If this is set, the duration specified by the %duration field is + * mandatory (and the driver should reject the scan request if it is + * unable to comply with the specified duration), otherwise it is the + * maximum duration and the actual duration may be shorter. + */ + unsigned int duration_mandatory:1; + + /** + * relative_rssi_set - Whether relative RSSI parameters are set + */ + unsigned int relative_rssi_set:1; + + /** + * relative_rssi - Relative RSSI for reporting better BSSs + * + * Amount of RSSI by which a BSS should be better than the current + * connected BSS to report the new BSS to user space. + */ + s8 relative_rssi; + + /** + * relative_adjust_band - Band to which RSSI should be adjusted + * + * The relative_adjust_rssi should be added to the band specified + * by relative_adjust_band. + */ + enum set_band relative_adjust_band; + + /** + * relative_adjust_rssi - RSSI to be added to relative_adjust_band + * + * An amount of relative_band_rssi should be added to the BSSs that + * belong to the band specified by relative_adjust_band while comparing + * with other bands for BSS reporting. + */ + s8 relative_adjust_rssi; + + /** + * oce_scan + * + * Enable the following OCE scan features: (WFA OCE TechSpec v1.0) + * - Accept broadcast Probe Response frame. + * - Probe Request frame deferral and suppression. + * - Max Channel Time - driver fills FILS request params IE with + * Maximum Channel Time. + * - Send 1st Probe Request frame in rate of minimum 5.5 Mbps. + */ + unsigned int oce_scan:1; + /* * NOTE: Whenever adding new parameters here, please make sure * wpa_scan_clone_params() and wpa_scan_free_params() get updated with @@ -485,17 +609,18 @@ struct wpa_driver_auth_params { int p2p; /** - * sae_data - SAE elements for Authentication frame + * auth_data - Additional elements for Authentication frame * * This buffer starts with the Authentication transaction sequence - * number field. If SAE is not used, this pointer is %NULL. + * number field. If no special handling of such elements is needed, this + * pointer is %NULL. This is used with SAE and FILS. */ - const u8 *sae_data; + const u8 *auth_data; /** - * sae_data_len - Length of sae_data buffer in octets + * auth_data_len - Length of auth_data buffer in octets */ - size_t sae_data_len; + size_t auth_data_len; }; /** @@ -577,6 +702,68 @@ struct hostapd_freq_params { }; /** + * struct wpa_driver_sta_auth_params - Authentication parameters + * Data for struct wpa_driver_ops::sta_auth(). + */ +struct wpa_driver_sta_auth_params { + + /** + * own_addr - Source address and BSSID for authentication frame + */ + const u8 *own_addr; + + /** + * addr - MAC address of the station to associate + */ + const u8 *addr; + + /** + * seq - authentication sequence number + */ + u16 seq; + + /** + * status - authentication response status code + */ + u16 status; + + /** + * ie - authentication frame ie buffer + */ + const u8 *ie; + + /** + * len - ie buffer length + */ + size_t len; + + /** + * fils_auth - Indicates whether FILS authentication is being performed + */ + int fils_auth; + + /** + * fils_anonce - ANonce (required for FILS) + */ + u8 fils_anonce[WPA_NONCE_LEN]; + + /** + * fils_snonce - SNonce (required for FILS) + */ + u8 fils_snonce[WPA_NONCE_LEN]; + + /** + * fils_kek - key for encryption (required for FILS) + */ + u8 fils_kek[WPA_KEK_MAX_LEN]; + + /** + * fils_kek_len - Length of the fils_kek in octets (required for FILS) + */ + size_t fils_kek_len; +}; + +/** * struct wpa_driver_associate_params - Association parameters * Data for struct wpa_driver_ops::associate(). */ @@ -639,7 +826,7 @@ struct wpa_driver_associate_params { * WPA information element to be included in (Re)Association * Request (including information element id and length). Use * of this WPA IE is optional. If the driver generates the WPA - * IE, it can use pairwise_suite, group_suite, and + * IE, it can use pairwise_suite, group_suite, group_mgmt_suite, and * key_mgmt_suite to select proper algorithms. In this case, * the driver has to notify wpa_supplicant about the used WPA * IE by generating an event that the interface code will @@ -679,6 +866,13 @@ struct wpa_driver_associate_params { unsigned int group_suite; /** + * mgmt_group_suite - Selected group management cipher suite (WPA_CIPHER_*) + * + * This is usually ignored if @wpa_ie is used. + */ + unsigned int mgmt_group_suite; + + /** * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*) * * This is usually ignored if @wpa_ie is used. @@ -717,43 +911,6 @@ struct wpa_driver_associate_params { enum mfp_options mgmt_frame_protection; /** - * ft_ies - IEEE 802.11r / FT information elements - * If the supplicant is using IEEE 802.11r (FT) and has the needed keys - * for fast transition, this parameter is set to include the IEs that - * are to be sent in the next FT Authentication Request message. - * update_ft_ies() handler is called to update the IEs for further - * FT messages in the sequence. - * - * The driver should use these IEs only if the target AP is advertising - * the same mobility domain as the one included in the MDIE here. - * - * In ap_scan=2 mode, the driver can use these IEs when moving to a new - * AP after the initial association. These IEs can only be used if the - * target AP is advertising support for FT and is using the same MDIE - * and SSID as the current AP. - * - * The driver is responsible for reporting the FT IEs received from the - * AP's response using wpa_supplicant_event() with EVENT_FT_RESPONSE - * type. update_ft_ies() handler will then be called with the FT IEs to - * include in the next frame in the authentication sequence. - */ - const u8 *ft_ies; - - /** - * ft_ies_len - Length of ft_ies in bytes - */ - size_t ft_ies_len; - - /** - * ft_md - FT Mobility domain (6 octets) (also included inside ft_ies) - * - * This value is provided to allow the driver interface easier access - * to the current mobility domain. This value is set to %NULL if no - * mobility domain is currently active. - */ - const u8 *ft_md; - - /** * passphrase - RSN passphrase for PSK * * This value is made available only for WPA/WPA2-Personal (PSK) and @@ -882,6 +1039,64 @@ struct wpa_driver_associate_params { * AP as usual. Valid for DMG network only. */ int pbss; + + /** + * fils_kek - KEK for FILS association frame protection (AES-SIV) + */ + const u8 *fils_kek; + + /** + * fils_kek_len: Length of fils_kek in bytes + */ + size_t fils_kek_len; + + /** + * fils_nonces - Nonces for FILS association frame protection + * (AES-SIV AAD) + */ + const u8 *fils_nonces; + + /** + * fils_nonces_len: Length of fils_nonce in bytes + */ + size_t fils_nonces_len; + + /** + * fils_erp_username - Username part of keyName-NAI + */ + const u8 *fils_erp_username; + + /** + * fils_erp_username_len - Length of fils_erp_username in bytes + */ + size_t fils_erp_username_len; + + /** + * fils_erp_realm - Realm/domain name to use in FILS ERP + */ + const u8 *fils_erp_realm; + + /** + * fils_erp_realm_len - Length of fils_erp_realm in bytes + */ + size_t fils_erp_realm_len; + + /** + * fils_erp_next_seq_num - The next sequence number to use in FILS ERP + * messages + */ + u16 fils_erp_next_seq_num; + + /** + * fils_erp_rrk - Re-authentication root key (rRK) for the keyName-NAI + * specified by fils_erp_username@fils_erp_realm. + */ + const u8 *fils_erp_rrk; + + /** + * fils_erp_rrk_len - Length of fils_erp_rrk in bytes + */ + size_t fils_erp_rrk_len; }; enum hide_ssid { @@ -940,6 +1155,22 @@ struct wpa_driver_ap_params { int *basic_rates; /** + * beacon_rate: Beacon frame data rate + * + * This parameter can be used to set a specific Beacon frame data rate + * for the BSS. The interpretation of this value depends on the + * rate_type (legacy: in 100 kbps units, HT: HT-MCS, VHT: VHT-MCS). If + * beacon_rate == 0 and rate_type == 0 (BEACON_RATE_LEGACY), the default + * Beacon frame data rate is used. + */ + unsigned int beacon_rate; + + /** + * beacon_rate_type: Beacon data rate type (legacy/HT/VHT) + */ + enum beacon_rate_type rate_type; + + /** * proberesp - Probe Response template * * This is used by drivers that reply to Probe Requests internally in @@ -1115,6 +1346,27 @@ struct wpa_driver_ap_params { * infrastructure BSS. Valid only for DMG network. */ int pbss; + + /** + * multicast_to_unicast - Whether to use multicast_to_unicast + * + * If this is non-zero, the AP is requested to perform multicast to + * unicast conversion for ARP, IPv4, and IPv6 frames (possibly within + * 802.1Q). If enabled, such frames are to be sent to each station + * separately, with the DA replaced by their own MAC address rather + * than the group address. + * + * Note that this may break certain expectations of the receiver, such + * as the ability to drop unicast IP packets received within multicast + * L2 frames, or the ability to not send ICMP destination unreachable + * messages for packets received in L2 multicast (which is required, + * but the receiver can't tell the difference if this new option is + * enabled.) + * + * This also doesn't implement the 802.11 DMS (directed multicast + * service). + */ + int multicast_to_unicast; }; struct wpa_driver_mesh_bss_params { @@ -1122,6 +1374,7 @@ struct wpa_driver_mesh_bss_params { #define WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT 0x00000002 #define WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS 0x00000004 #define WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE 0x00000008 +#define WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD 0x00000010 /* * TODO: Other mesh configuration parameters would go here. * See NL80211_MESHCONF_* for all the mesh config parameters. @@ -1130,6 +1383,7 @@ struct wpa_driver_mesh_bss_params { int auto_plinks; int peer_link_timeout; int max_peer_links; + int rssi_threshold; u16 ht_opmode; }; @@ -1164,6 +1418,12 @@ struct wpa_driver_capa { #define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK 0x00000080 #define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B 0x00000100 #define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192 0x00000200 +#define WPA_DRIVER_CAPA_KEY_MGMT_OWE 0x00000400 +#define WPA_DRIVER_CAPA_KEY_MGMT_DPP 0x00000800 +#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 0x00001000 +#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 0x00002000 +#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 0x00004000 +#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 0x00008000 /** Bitfield of supported key management suites */ unsigned int key_mgmt; @@ -1286,6 +1546,39 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE 0x0000010000000000ULL /** Driver supports P2P Listen offload */ #define WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD 0x0000020000000000ULL +/** Driver supports FILS */ +#define WPA_DRIVER_FLAGS_SUPPORT_FILS 0x0000040000000000ULL +/** Driver supports Beacon frame TX rate configuration (legacy rates) */ +#define WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY 0x0000080000000000ULL +/** Driver supports Beacon frame TX rate configuration (HT rates) */ +#define WPA_DRIVER_FLAGS_BEACON_RATE_HT 0x0000100000000000ULL +/** Driver supports Beacon frame TX rate configuration (VHT rates) */ +#define WPA_DRIVER_FLAGS_BEACON_RATE_VHT 0x0000200000000000ULL +/** Driver supports mgmt_tx with random TX address in non-connected state */ +#define WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA 0x0000400000000000ULL +/** Driver supports mgmt_tx with random TX addr in connected state */ +#define WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED 0x0000800000000000ULL +/** Driver supports better BSS reporting with sched_scan in connected mode */ +#define WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI 0x0001000000000000ULL +/** Driver supports HE capabilities */ +#define WPA_DRIVER_FLAGS_HE_CAPABILITIES 0x0002000000000000ULL +/** Driver supports FILS shared key offload */ +#define WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD 0x0004000000000000ULL +/** Driver supports all OCE STA specific mandatory features */ +#define WPA_DRIVER_FLAGS_OCE_STA 0x0008000000000000ULL +/** Driver supports all OCE AP specific mandatory features */ +#define WPA_DRIVER_FLAGS_OCE_AP 0x0010000000000000ULL +/** + * Driver supports all OCE STA-CFON specific mandatory features only. + * If a driver sets this bit but not the %WPA_DRIVER_FLAGS_OCE_AP, the + * userspace shall assume that this driver may not support all OCE AP + * functionality but can support only OCE STA-CFON functionality. + */ +#define WPA_DRIVER_FLAGS_OCE_STA_CFON 0x0020000000000000ULL +/** Driver supports MFP-optional in the connect command */ +#define WPA_DRIVER_FLAGS_MFP_OPTIONAL 0x0040000000000000ULL +/** Driver is a self-managed regulatory device */ +#define WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY 0x0080000000000000ULL u64 flags; #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \ @@ -1386,6 +1679,11 @@ struct wpa_driver_capa { */ #define WPA_DRIVER_FLAGS_SUPPORT_RRM 0x00000010 +/** Driver supports setting the scan dwell time */ +#define WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL 0x00000020 +/** Driver supports Beacon Report Measurement */ +#define WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT 0x00000040 + u32 rrm_flags; /* Driver concurrency capabilities */ @@ -1402,18 +1700,35 @@ struct wpa_driver_capa { struct hostapd_data; +#define STA_DRV_DATA_TX_MCS BIT(0) +#define STA_DRV_DATA_RX_MCS BIT(1) +#define STA_DRV_DATA_TX_VHT_MCS BIT(2) +#define STA_DRV_DATA_RX_VHT_MCS BIT(3) +#define STA_DRV_DATA_TX_VHT_NSS BIT(4) +#define STA_DRV_DATA_RX_VHT_NSS BIT(5) +#define STA_DRV_DATA_TX_SHORT_GI BIT(6) +#define STA_DRV_DATA_RX_SHORT_GI BIT(7) +#define STA_DRV_DATA_LAST_ACK_RSSI BIT(8) + struct hostap_sta_driver_data { unsigned long rx_packets, tx_packets; unsigned long long rx_bytes, tx_bytes; int bytes_64bit; /* whether 64-bit byte counters are supported */ unsigned long current_tx_rate; + unsigned long current_rx_rate; unsigned long inactive_msec; - unsigned long flags; + unsigned long flags; /* bitfield of STA_DRV_DATA_* */ unsigned long num_ps_buf_frames; unsigned long tx_retry_failed; unsigned long tx_retry_count; - int last_rssi; - int last_ack_rssi; + s8 last_ack_rssi; + s8 signal; + u8 rx_vhtmcs; + u8 tx_vhtmcs; + u8 rx_mcs; + u8 tx_mcs; + u8 rx_vht_nss; + u8 tx_vht_nss; }; struct hostapd_sta_add_params { @@ -1576,6 +1891,17 @@ enum wnm_oper { WNM_SLEEP_TFS_IE_DEL /* AP delete the TFS IE */ }; +/* enum smps_mode - SMPS mode definitions */ +enum smps_mode { + SMPS_AUTOMATIC, + SMPS_OFF, + SMPS_DYNAMIC, + SMPS_STATIC, + + /* Keep last */ + SMPS_INVALID, +}; + /* enum chan_width - Channel width definitions */ enum chan_width { CHAN_WIDTH_20_NOHT, @@ -1587,8 +1913,21 @@ enum chan_width { CHAN_WIDTH_UNKNOWN }; +#define WPA_INVALID_NOISE 9999 + /** * struct wpa_signal_info - Information about channel signal quality + * @frequency: control frequency + * @above_threshold: true if the above threshold was crossed + * (relevant for a CQM event) + * @current_signal: in dBm + * @avg_signal: in dBm + * @avg_beacon_signal: in dBm + * @current_noise: %WPA_INVALID_NOISE if not supported + * @current_txrate: current TX rate + * @chanwidth: channel width + * @center_frq1: center frequency for the first segment + * @center_frq2: center frequency for the second segment (if relevant) */ struct wpa_signal_info { u32 frequency; @@ -1720,6 +2059,67 @@ struct drv_acs_params { const int *freq_list; }; +struct wpa_bss_trans_info { + u8 mbo_transition_reason; + u8 n_candidates; + u8 *bssid; +}; + +struct wpa_bss_candidate_info { + u8 num; + struct candidate_list { + u8 bssid[ETH_ALEN]; + u8 is_accept; + u32 reject_reason; + } *candidates; +}; + +struct wpa_pmkid_params { + const u8 *bssid; + const u8 *ssid; + size_t ssid_len; + const u8 *fils_cache_id; + const u8 *pmkid; + const u8 *pmk; + size_t pmk_len; +}; + +/* Mask used to specify which connection parameters have to be updated */ +enum wpa_drv_update_connect_params_mask { + WPA_DRV_UPDATE_ASSOC_IES = BIT(0), + WPA_DRV_UPDATE_FILS_ERP_INFO = BIT(1), + WPA_DRV_UPDATE_AUTH_TYPE = BIT(2), +}; + +/** + * struct external_auth - External authentication trigger parameters + * + * These are used across the external authentication request and event + * interfaces. + * @action: Action type / trigger for external authentication. Only significant + * for the event interface. + * @bssid: BSSID of the peer with which the authentication has to happen. Used + * by both the request and event interface. + * @ssid: SSID of the AP. Used by both the request and event interface. + * @ssid_len: SSID length in octets. + * @key_mgmt_suite: AKM suite of the respective authentication. Optional for + * the request interface. + * @status: Status code, %WLAN_STATUS_SUCCESS for successful authentication, + * use %WLAN_STATUS_UNSPECIFIED_FAILURE if wpa_supplicant cannot give + * the real status code for failures. Used only for the request interface + * from user space to the driver. + */ +struct external_auth { + enum { + EXT_AUTH_START, + EXT_AUTH_ABORT, + } action; + u8 bssid[ETH_ALEN]; + u8 ssid[SSID_MAX_LEN]; + size_t ssid_len; + unsigned int key_mgmt_suite; + u16 status; +}; /** * struct wpa_driver_ops - Driver interface API definition @@ -1902,13 +2302,14 @@ struct wpa_driver_ops { /** * add_pmkid - Add PMKSA cache entry to the driver * @priv: private driver interface data - * @bssid: BSSID for the PMKSA cache entry - * @pmkid: PMKID for the PMKSA cache entry + * @params: PMKSA parameters * * Returns: 0 on success, -1 on failure * * This function is called when a new PMK is received, as a result of - * either normal authentication or RSN pre-authentication. + * either normal authentication or RSN pre-authentication. The PMKSA + * parameters are either a set of bssid, pmkid, and pmk; or a set of + * ssid, fils_cache_id, pmkid, and pmk. * * If the driver generates RSN IE, i.e., it does not use wpa_ie in * associate(), add_pmkid() can be used to add new PMKSA cache entries @@ -1916,18 +2317,18 @@ struct wpa_driver_ops { * driver_ops function does not need to be implemented. Likewise, if * the driver does not support WPA, this function is not needed. */ - int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid); + int (*add_pmkid)(void *priv, struct wpa_pmkid_params *params); /** * remove_pmkid - Remove PMKSA cache entry to the driver * @priv: private driver interface data - * @bssid: BSSID for the PMKSA cache entry - * @pmkid: PMKID for the PMKSA cache entry + * @params: PMKSA parameters * * Returns: 0 on success, -1 on failure * * This function is called when the supplicant drops a PMKSA cache - * entry for any reason. + * entry for any reason. The PMKSA parameters are either a set of + * bssid and pmkid; or a set of ssid, fils_cache_id, and pmkid. * * If the driver generates RSN IE, i.e., it does not use wpa_ie in * associate(), remove_pmkid() can be used to synchronize PMKSA caches @@ -1936,7 +2337,7 @@ struct wpa_driver_ops { * implemented. Likewise, if the driver does not support WPA, this * function is not needed. */ - int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid); + int (*remove_pmkid)(void *priv, struct wpa_pmkid_params *params); /** * flush_pmkid - Flush PMKSA cache @@ -2051,12 +2452,13 @@ struct wpa_driver_ops { * @priv: Private driver interface data * @num_modes: Variable for returning the number of returned modes * flags: Variable for returning hardware feature flags + * @dfs: Variable for returning DFS region (HOSTAPD_DFS_REGION_*) * Returns: Pointer to allocated hardware data on success or %NULL on * failure. Caller is responsible for freeing this. */ struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv, u16 *num_modes, - u16 *flags); + u16 *flags, u8 *dfs); /** * send_mlme - Send management frame from MLME @@ -2673,6 +3075,9 @@ struct wpa_driver_ops { * transmitted on that channel; alternatively the frame may be sent on * the current operational channel (if in associated state in station * mode or while operating as an AP.) + * + * If @src differs from the device MAC address, use of a random + * transmitter address is requested for this message exchange. */ int (*send_action)(void *priv, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *src, const u8 *bssid, @@ -3058,19 +3463,13 @@ struct wpa_driver_ops { /** * sta_auth - Station authentication indication - * @priv: Private driver interface data - * @own_addr: Source address and BSSID for authentication frame - * @addr: MAC address of the station to associate - * @seq: authentication sequence number - * @status: authentication response status code - * @ie: authentication frame ie buffer - * @len: ie buffer length + * @priv: private driver interface data + * @params: Station authentication parameters * - * This function indicates the driver to send Authentication frame - * to the station. + * Returns: 0 on success, -1 on failure */ - int (*sta_auth)(void *priv, const u8 *own_addr, const u8 *addr, - u16 seq, u16 status, const u8 *ie, size_t len); + int (*sta_auth)(void *priv, + struct wpa_driver_sta_auth_params *params); /** * add_tspec - Add traffic stream @@ -3282,6 +3681,17 @@ struct wpa_driver_ops { int (*roaming)(void *priv, int allowed, const u8 *bssid); /** + * disable_fils - Enable/disable FILS feature + * @priv: Private driver interface data + * @disable: 0-enable and 1-disable FILS feature + * Returns: 0 on success, -1 on failure + * + * This callback can be used to configure driver and below layers to + * enable/disable all FILS features. + */ + int (*disable_fils)(void *priv, int disable); + + /** * set_mac_addr - Set MAC address * @priv: Private driver interface data * @addr: MAC address to use or %NULL for setting back to permanent @@ -3295,6 +3705,14 @@ struct wpa_driver_ops { int (*macsec_deinit)(void *priv); /** + * macsec_get_capability - Inform MKA of this driver's capability + * @priv: Private driver interface data + * @cap: Driver's capability + * Returns: 0 on success, -1 on failure + */ + int (*macsec_get_capability)(void *priv, enum macsec_cap *cap); + + /** * enable_protect_frames - Set protect frames status * @priv: Private driver interface data * @enabled: TRUE = protect frames enabled @@ -3304,6 +3722,15 @@ struct wpa_driver_ops { int (*enable_protect_frames)(void *priv, Boolean enabled); /** + * enable_encrypt - Set encryption status + * @priv: Private driver interface data + * @enabled: TRUE = encrypt outgoing traffic + * FALSE = integrity-only protection on outgoing traffic + * Returns: 0 on success, -1 on failure (or if not supported) + */ + int (*enable_encrypt)(void *priv, Boolean enabled); + + /** * set_replay_protect - Set replay protect status and window size * @priv: Private driver interface data * @enabled: TRUE = replay protect enabled @@ -3333,155 +3760,129 @@ struct wpa_driver_ops { /** * get_receive_lowest_pn - Get receive lowest pn * @priv: Private driver interface data - * @channel: secure channel - * @an: association number - * @lowest_pn: lowest accept pn + * @sa: secure association * Returns: 0 on success, -1 on failure (or if not supported) */ - int (*get_receive_lowest_pn)(void *priv, u32 channel, u8 an, - u32 *lowest_pn); + int (*get_receive_lowest_pn)(void *priv, struct receive_sa *sa); /** * get_transmit_next_pn - Get transmit next pn * @priv: Private driver interface data - * @channel: secure channel - * @an: association number - * @next_pn: next pn + * @sa: secure association * Returns: 0 on success, -1 on failure (or if not supported) */ - int (*get_transmit_next_pn)(void *priv, u32 channel, u8 an, - u32 *next_pn); + int (*get_transmit_next_pn)(void *priv, struct transmit_sa *sa); /** * set_transmit_next_pn - Set transmit next pn * @priv: Private driver interface data - * @channel: secure channel - * @an: association number - * @next_pn: next pn + * @sa: secure association * Returns: 0 on success, -1 on failure (or if not supported) */ - int (*set_transmit_next_pn)(void *priv, u32 channel, u8 an, - u32 next_pn); - - /** - * get_available_receive_sc - get available receive channel - * @priv: Private driver interface data - * @channel: secure channel - * Returns: 0 on success, -1 on failure (or if not supported) - */ - int (*get_available_receive_sc)(void *priv, u32 *channel); + int (*set_transmit_next_pn)(void *priv, struct transmit_sa *sa); /** * create_receive_sc - create secure channel for receiving * @priv: Private driver interface data - * @channel: secure channel - * @sci_addr: secure channel identifier - address - * @sci_port: secure channel identifier - port + * @sc: secure channel * @conf_offset: confidentiality offset (0, 30, or 50) * @validation: frame validation policy (0 = Disabled, 1 = Checked, * 2 = Strict) * Returns: 0 on success, -1 on failure (or if not supported) */ - int (*create_receive_sc)(void *priv, u32 channel, const u8 *sci_addr, - u16 sci_port, unsigned int conf_offset, + int (*create_receive_sc)(void *priv, struct receive_sc *sc, + unsigned int conf_offset, int validation); /** * delete_receive_sc - delete secure connection for receiving * @priv: private driver interface data from init() - * @channel: secure channel + * @sc: secure channel * Returns: 0 on success, -1 on failure */ - int (*delete_receive_sc)(void *priv, u32 channel); + int (*delete_receive_sc)(void *priv, struct receive_sc *sc); /** * create_receive_sa - create secure association for receive * @priv: private driver interface data from init() - * @channel: secure channel - * @an: association number - * @lowest_pn: the lowest packet number can be received - * @sak: the secure association key + * @sa: secure association * Returns: 0 on success, -1 on failure */ - int (*create_receive_sa)(void *priv, u32 channel, u8 an, - u32 lowest_pn, const u8 *sak); + int (*create_receive_sa)(void *priv, struct receive_sa *sa); /** - * enable_receive_sa - enable the SA for receive - * @priv: private driver interface data from init() - * @channel: secure channel - * @an: association number + * delete_receive_sa - Delete secure association for receive + * @priv: Private driver interface data from init() + * @sa: Secure association * Returns: 0 on success, -1 on failure */ - int (*enable_receive_sa)(void *priv, u32 channel, u8 an); + int (*delete_receive_sa)(void *priv, struct receive_sa *sa); /** - * disable_receive_sa - disable SA for receive + * enable_receive_sa - enable the SA for receive * @priv: private driver interface data from init() - * @channel: secure channel index - * @an: association number + * @sa: secure association * Returns: 0 on success, -1 on failure */ - int (*disable_receive_sa)(void *priv, u32 channel, u8 an); + int (*enable_receive_sa)(void *priv, struct receive_sa *sa); /** - * get_available_transmit_sc - get available transmit channel - * @priv: Private driver interface data - * @channel: secure channel - * Returns: 0 on success, -1 on failure (or if not supported) + * disable_receive_sa - disable SA for receive + * @priv: private driver interface data from init() + * @sa: secure association + * Returns: 0 on success, -1 on failure */ - int (*get_available_transmit_sc)(void *priv, u32 *channel); + int (*disable_receive_sa)(void *priv, struct receive_sa *sa); /** * create_transmit_sc - create secure connection for transmit * @priv: private driver interface data from init() - * @channel: secure channel - * @sci_addr: secure channel identifier - address - * @sci_port: secure channel identifier - port + * @sc: secure channel + * @conf_offset: confidentiality offset (0, 30, or 50) * Returns: 0 on success, -1 on failure */ - int (*create_transmit_sc)(void *priv, u32 channel, const u8 *sci_addr, - u16 sci_port, unsigned int conf_offset); + int (*create_transmit_sc)(void *priv, struct transmit_sc *sc, + unsigned int conf_offset); /** * delete_transmit_sc - delete secure connection for transmit * @priv: private driver interface data from init() - * @channel: secure channel + * @sc: secure channel * Returns: 0 on success, -1 on failure */ - int (*delete_transmit_sc)(void *priv, u32 channel); + int (*delete_transmit_sc)(void *priv, struct transmit_sc *sc); /** * create_transmit_sa - create secure association for transmit * @priv: private driver interface data from init() - * @channel: secure channel index - * @an: association number - * @next_pn: the packet number used as next transmit packet - * @confidentiality: True if the SA is to provide confidentiality - * as well as integrity - * @sak: the secure association key + * @sa: secure association + * Returns: 0 on success, -1 on failure + */ + int (*create_transmit_sa)(void *priv, struct transmit_sa *sa); + + /** + * delete_transmit_sa - Delete secure association for transmit + * @priv: Private driver interface data from init() + * @sa: Secure association * Returns: 0 on success, -1 on failure */ - int (*create_transmit_sa)(void *priv, u32 channel, u8 an, u32 next_pn, - Boolean confidentiality, const u8 *sak); + int (*delete_transmit_sa)(void *priv, struct transmit_sa *sa); /** * enable_transmit_sa - enable SA for transmit * @priv: private driver interface data from init() - * @channel: secure channel - * @an: association number + * @sa: secure association * Returns: 0 on success, -1 on failure */ - int (*enable_transmit_sa)(void *priv, u32 channel, u8 an); + int (*enable_transmit_sa)(void *priv, struct transmit_sa *sa); /** * disable_transmit_sa - disable SA for transmit * @priv: private driver interface data from init() - * @channel: secure channel - * @an: association number + * @sa: secure association * Returns: 0 on success, -1 on failure */ - int (*disable_transmit_sa)(void *priv, u32 channel, u8 an); + int (*disable_transmit_sa)(void *priv, struct transmit_sa *sa); #endif /* CONFIG_MACSEC */ /** @@ -3555,9 +3956,12 @@ struct wpa_driver_ops { /** * abort_scan - Request the driver to abort an ongoing scan * @priv: Private driver interface data + * @scan_cookie: Cookie identifying the scan request. This is used only + * when the vendor interface QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN + * was used to trigger scan. Otherwise, 0 is used. * Returns 0 on success, -1 on failure */ - int (*abort_scan)(void *priv); + int (*abort_scan)(void *priv, u64 scan_cookie); /** * configure_data_frame_filters - Request to configure frame filters @@ -3623,8 +4027,72 @@ struct wpa_driver_ops { */ int (*set_default_scan_ies)(void *priv, const u8 *ies, size_t ies_len); -}; + /** + * set_tdls_mode - Set TDLS trigger mode to the host driver + * @priv: Private driver interface data + * @tdls_external_control: Represents if TDLS external trigger control + * mode is enabled/disabled. + * + * This optional callback can be used to configure the TDLS external + * trigger control mode to the host driver. + */ + int (*set_tdls_mode)(void *priv, int tdls_external_control); + /** + * get_bss_transition_status - Get candidate BSS's transition status + * @priv: Private driver interface data + * @params: Candidate BSS list + * + * Get the accept or reject reason code for a list of BSS transition + * candidates. + */ + struct wpa_bss_candidate_info * + (*get_bss_transition_status)(void *priv, + struct wpa_bss_trans_info *params); + /** + * ignore_assoc_disallow - Configure driver to ignore assoc_disallow + * @priv: Private driver interface data + * @ignore_disallow: 0 to not ignore, 1 to ignore + * Returns: 0 on success, -1 on failure + */ + int (*ignore_assoc_disallow)(void *priv, int ignore_disallow); + + /** + * set_bssid_blacklist - Set blacklist of BSSIDs to the driver + * @priv: Private driver interface data + * @num_bssid: Number of blacklist BSSIDs + * @bssids: List of blacklisted BSSIDs + */ + int (*set_bssid_blacklist)(void *priv, unsigned int num_bssid, + const u8 *bssid); + + /** + * update_connect_params - Update the connection parameters + * @priv: Private driver interface data + * @params: Association parameters + * @mask: Bit mask indicating which parameters in @params have to be + * updated + * Returns: 0 on success, -1 on failure + * + * Update the connection parameters when in connected state so that the + * driver uses the updated parameters for subsequent roaming. This is + * used only with drivers that implement internal BSS selection and + * roaming. + */ + int (*update_connect_params)( + void *priv, struct wpa_driver_associate_params *params, + enum wpa_drv_update_connect_params_mask mask); + + /** + * send_external_auth_status - Indicate the status of external + * authentication processing to the host driver. + * @priv: Private driver interface data + * @params: Status of authentication processing. + * Returns: 0 on success, -1 on failure + */ + int (*send_external_auth_status)(void *priv, + struct external_auth *params); +}; /** * enum wpa_event_type - Event type for wpa_supplicant_event() calls @@ -3734,17 +4202,6 @@ enum wpa_event_type { EVENT_PMKID_CANDIDATE, /** - * EVENT_STKSTART - Request STK handshake (MLME-STKSTART.request) - * - * This event can be used to inform wpa_supplicant about desire to set - * up secure direct link connection between two stations as defined in - * IEEE 802.11e with a new PeerKey mechanism that replaced the original - * STAKey negotiation. The caller will need to set peer address for the - * event. - */ - EVENT_STKSTART, - - /** * EVENT_TDLS - Request TDLS operation * * This event can be used to request a TDLS operation to be performed. @@ -4043,7 +4500,7 @@ enum wpa_event_type { * EVENT_DFS_CAC_ABORTED - Notify that channel availability check has been aborted * * The CAC was not successful, and the channel remains in the previous - * state. This may happen due to a radar beeing detected or other + * state. This may happen due to a radar being detected or other * external influences. */ EVENT_DFS_CAC_ABORTED, @@ -4112,6 +4569,65 @@ enum wpa_event_type { * EVENT_P2P_LO_STOP - Notify that P2P listen offload is stopped */ EVENT_P2P_LO_STOP, + + /** + * EVENT_BEACON_LOSS - Beacon loss detected + * + * This event indicates that no Beacon frames has been received from + * the current AP. This may indicate that the AP is not anymore in + * range. + */ + EVENT_BEACON_LOSS, + + /** + * EVENT_DFS_PRE_CAC_EXPIRED - Notify that channel availability check + * done previously (Pre-CAC) on the channel has expired. This would + * normally be on a non-ETSI DFS regulatory domain. DFS state of the + * channel will be moved from available to usable. A new CAC has to be + * performed before start operating on this channel. + */ + EVENT_DFS_PRE_CAC_EXPIRED, + + /** + * EVENT_EXTERNAL_AUTH - This event interface is used by host drivers + * that do not define separate commands for authentication and + * association (~WPA_DRIVER_FLAGS_SME) but offload the 802.11 + * authentication to wpa_supplicant. This event carries all the + * necessary information from the host driver for the authentication to + * happen. + */ + EVENT_EXTERNAL_AUTH, + + /** + * EVENT_PORT_AUTHORIZED - Notification that a connection is authorized + * + * This event should be indicated when the driver completes the 4-way + * handshake. This event should be preceded by an EVENT_ASSOC that + * indicates the completion of IEEE 802.11 association. + */ + EVENT_PORT_AUTHORIZED, + + /** + * EVENT_STATION_OPMODE_CHANGED - Notify STA's HT/VHT operation mode + * change event. + */ + EVENT_STATION_OPMODE_CHANGED, + + /** + * EVENT_INTERFACE_MAC_CHANGED - Notify that interface MAC changed + * + * This event is emitted when the MAC changes while the interface is + * enabled. When an interface was disabled and becomes enabled, it + * must be always assumed that the MAC possibly changed. + */ + EVENT_INTERFACE_MAC_CHANGED, + + /** + * EVENT_WDS_STA_INTERFACE_STATUS - Notify WDS STA interface status + * + * This event is emitted when an interface is added/removed for WDS STA. + */ + EVENT_WDS_STA_INTERFACE_STATUS, }; @@ -4204,6 +4720,16 @@ union wpa_event_data { size_t resp_ies_len; /** + * resp_frame - (Re)Association Response frame + */ + const u8 *resp_frame; + + /** + * resp_frame_len - (Re)Association Response frame length + */ + size_t resp_frame_len; + + /** * beacon_ies - Beacon or Probe Response IEs * * Optional Beacon/ProbeResp data: IEs included in Beacon or @@ -4280,6 +4806,8 @@ union wpa_event_data { /** * ptk_kek - The derived PTK KEK + * This is used in key management offload and also in FILS SK + * offload. */ const u8 *ptk_kek; @@ -4293,6 +4821,36 @@ union wpa_event_data { * 0 = unknown, 1 = unchanged, 2 = changed */ u8 subnet_status; + + /** + * The following information is used in FILS SK offload + * @fils_erp_next_seq_num + * @fils_pmk + * @fils_pmk_len + * @fils_pmkid + */ + + /** + * fils_erp_next_seq_num - The next sequence number to use in + * FILS ERP messages + */ + u16 fils_erp_next_seq_num; + + /** + * fils_pmk - A new PMK if generated in case of FILS + * authentication + */ + const u8 *fils_pmk; + + /** + * fils_pmk_len - Length of fils_pmk + */ + size_t fils_pmk_len; + + /** + * fils_pmkid - PMKID used or generated in FILS authentication + */ + const u8 *fils_pmkid; } assoc_info; /** @@ -4389,13 +4947,6 @@ union wpa_event_data { } pmkid_candidate; /** - * struct stkstart - Data for EVENT_STKSTART - */ - struct stkstart { - u8 peer[ETH_ALEN]; - } stkstart; - - /** * struct tdls - Data for EVENT_TDLS */ struct tdls { @@ -4503,6 +5054,17 @@ union wpa_event_data { * than explicit rejection response from the AP. */ int timed_out; + + /** + * timeout_reason - Reason for the timeout + */ + const char *timeout_reason; + + /** + * fils_erp_next_seq_num - The next sequence number to use in + * FILS ERP messages + */ + u16 fils_erp_next_seq_num; } assoc_reject; struct timeout_event { @@ -4586,6 +5148,11 @@ union wpa_event_data { * @external_scan: Whether the scan info is for an external scan * @nl_scan_event: 1 if the source of this scan event is a normal scan, * 0 if the source of the scan event is a vendor scan + * @scan_start_tsf: Time when the scan started in terms of TSF of the + * BSS that the interface that requested the scan is connected to + * (if available). + * @scan_start_tsf_bssid: The BSSID according to which %scan_start_tsf + * is set. */ struct scan_info { int aborted; @@ -4595,6 +5162,8 @@ union wpa_event_data { size_t num_ssids; int external_scan; int nl_scan_event; + u64 scan_start_tsf; + u8 scan_start_tsf_bssid[ETH_ALEN]; } scan_info; /** @@ -4684,9 +5253,12 @@ union wpa_event_data { /** * struct low_ack - Data for EVENT_STATION_LOW_ACK events * @addr: station address + * @num_packets: Number of packets lost (consecutive packets not + * acknowledged) */ struct low_ack { u8 addr[ETH_ALEN]; + u32 num_packets; } low_ack; /** @@ -4858,6 +5430,37 @@ union wpa_event_data { P2P_LO_STOPPED_REASON_NOT_SUPPORTED, } reason_code; } p2p_lo_stop; + + /* For EVENT_EXTERNAL_AUTH */ + struct external_auth external_auth; + + /** + * struct sta_opmode - Station's operation mode change event + * @addr: The station MAC address + * @smps_mode: SMPS mode of the station + * @chan_width: Channel width of the station + * @rx_nss: RX_NSS of the station + * + * This is used as data with EVENT_STATION_OPMODE_CHANGED. + */ + struct sta_opmode { + const u8 *addr; + enum smps_mode smps_mode; + enum chan_width chan_width; + u8 rx_nss; + } sta_opmode; + + /** + * struct wds_sta_interface - Data for EVENT_WDS_STA_INTERFACE_STATUS. + */ + struct wds_sta_interface { + const u8 *sta_addr; + const char *ifname; + enum { + INTERFACE_ADDED, + INTERFACE_REMOVED + } istatus; + } wds_sta_interface; }; /** @@ -4973,6 +5576,10 @@ extern const struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */ /* driver_macsec_qca.c */ extern const struct wpa_driver_ops wpa_driver_macsec_qca_ops; #endif /* CONFIG_DRIVER_MACSEC_QCA */ +#ifdef CONFIG_DRIVER_MACSEC_LINUX +/* driver_macsec_linux.c */ +extern const struct wpa_driver_ops wpa_driver_macsec_linux_ops; +#endif /* CONFIG_DRIVER_MACSEC_LINUX */ #ifdef CONFIG_DRIVER_ROBOSWITCH /* driver_roboswitch.c */ extern const struct wpa_driver_ops wpa_driver_roboswitch_ops; diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c index a88345fc92f8..62f5baad6361 100644 --- a/src/drivers/driver_atheros.c +++ b/src/drivers/driver_atheros.c @@ -36,6 +36,10 @@ #include "ieee80211_external.h" +/* Avoid conflicting definition from the driver header files with + * common/wpa_common.h */ +#undef WPA_OUI_TYPE + #ifdef CONFIG_WPS #include <netpacket/packet.h> @@ -55,7 +59,7 @@ #include "netlink.h" #include "linux_ioctl.h" -#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) || defined(CONFIG_WNM) || defined(CONFIG_WPS) +#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) || defined(CONFIG_WNM) || defined(CONFIG_WPS) || defined(CONFIG_FILS) #define ATHEROS_USE_RAW_RECEIVE #endif @@ -70,6 +74,7 @@ struct atheros_driver_data { int ioctl_sock; /* socket for ioctl() use */ struct netlink_data *netlink; int we_version; + int fils_en; /* FILS enable/disable in driver */ u8 acct_mac[ETH_ALEN]; struct hostap_sta_driver_data acct_data; @@ -178,6 +183,25 @@ static const char * athr_get_param_name(int op) } +#ifdef CONFIG_FILS +static int +get80211param(struct atheros_driver_data *drv, int op, int *data) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.mode = op; + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_GETPARAM, &iwr) < 0) + return -1; + + *data = iwr.u.mode; + return 0; +} +#endif /* CONFIG_FILS */ + + static int set80211priv(struct atheros_driver_data *drv, int op, void *data, int len) { @@ -692,10 +716,14 @@ atheros_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) wpa_hexdump(MSG_DEBUG, "atheros: set_generic_elem", ie, ie_len); wpabuf_free(drv->wpa_ie); - drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len); + if (ie) + drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len); + else + drv->wpa_ie = NULL; app_ie = (struct ieee80211req_getset_appiebuf *) buf; - os_memcpy(&(app_ie->app_buf[0]), ie, ie_len); + if (ie) + os_memcpy(&(app_ie->app_buf[0]), ie, ie_len); app_ie->app_buflen = ie_len; app_ie->app_frmtype = IEEE80211_APPIE_FRAME_BEACON; @@ -903,6 +931,12 @@ static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) break; os_memset(&event, 0, sizeof(event)); + if (le_to_host16(mgmt->u.auth.auth_alg) == WLAN_AUTH_SAE) { + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); + break; + } os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); os_memcpy(event.auth.bssid, mgmt->bssid, ETH_ALEN); event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); @@ -931,11 +965,11 @@ static int atheros_receive_pkt(struct atheros_driver_data *drv) #ifdef CONFIG_WPS filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ; #endif /* CONFIG_WPS */ -#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) +#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS) filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ | IEEE80211_FILTER_TYPE_AUTH | IEEE80211_FILTER_TYPE_ACTION); -#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */ #ifdef CONFIG_WNM filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; #endif /* CONFIG_WNM */ @@ -949,12 +983,12 @@ static int atheros_receive_pkt(struct atheros_driver_data *drv) return ret; } -#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) +#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS) drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, atheros_raw_receive, drv, 1); if (drv->sock_raw == NULL) return -1; -#endif /* CONFIG_WPS || CONFIG_IEEE80211R */ +#endif /* CONFIG_WPS || CONFIG_IEEE80211R || CONFIG_FILS */ return ret; } @@ -981,7 +1015,8 @@ atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) beac_ie = (struct ieee80211req_getset_appiebuf *) buf; beac_ie->app_frmtype = frametype; beac_ie->app_buflen = len; - os_memcpy(&(beac_ie->app_buf[0]), ie, len); + if (ie) + os_memcpy(&(beac_ie->app_buf[0]), ie, len); /* append the WPA/RSN IE if it is set already */ if (((frametype == IEEE80211_APPIE_FRAME_BEACON) || @@ -1034,32 +1069,56 @@ atheros_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, #define atheros_set_ap_wps_ie NULL #endif /* CONFIG_WPS */ -#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) static int -atheros_sta_auth(void *priv, const u8 *own_addr, const u8 *addr, u16 seq, - u16 status_code, const u8 *ie, size_t len) +atheros_sta_auth(void *priv, struct wpa_driver_sta_auth_params *params) { struct atheros_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d", - __func__, ether_sprintf(addr), status_code); + __func__, ether_sprintf(params->addr), params->status); +#ifdef CONFIG_FILS + /* Copy FILS AAD parameters if the driver supports FILS */ + if (params->fils_auth && drv->fils_en) { + wpa_printf(MSG_DEBUG, "%s: im_op IEEE80211_MLME_AUTH_FILS", + __func__); + os_memcpy(mlme.fils_aad.ANonce, params->fils_anonce, + IEEE80211_FILS_NONCE_LEN); + os_memcpy(mlme.fils_aad.SNonce, params->fils_snonce, + IEEE80211_FILS_NONCE_LEN); + os_memcpy(mlme.fils_aad.kek, params->fils_kek, + IEEE80211_MAX_WPA_KEK_LEN); + mlme.fils_aad.kek_len = params->fils_kek_len; + mlme.im_op = IEEE80211_MLME_AUTH_FILS; + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", + mlme.fils_aad.ANonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: SNonce", + mlme.fils_aad.SNonce, FILS_NONCE_LEN); + wpa_hexdump_key(MSG_DEBUG, "FILS: KEK", + mlme.fils_aad.kek, mlme.fils_aad.kek_len); + } else { + mlme.im_op = IEEE80211_MLME_AUTH; + } +#else /* CONFIG_FILS */ mlme.im_op = IEEE80211_MLME_AUTH; - mlme.im_reason = status_code; - mlme.im_seq = seq; - os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - mlme.im_optie_len = len; - if (len) { - if (len < IEEE80211_MAX_OPT_IE) { - os_memcpy(mlme.im_optie, ie, len); +#endif /* CONFIG_FILS */ + + mlme.im_reason = params->status; + mlme.im_seq = params->seq; + os_memcpy(mlme.im_macaddr, params->addr, IEEE80211_ADDR_LEN); + mlme.im_optie_len = params->len; + if (params->len) { + if (params->len < IEEE80211_MAX_OPT_IE) { + os_memcpy(mlme.im_optie, params->ie, params->len); } else { wpa_printf(MSG_DEBUG, "%s: Not enough space to copy " "opt_ie STA (addr " MACSTR " reason %d, " "ie_len %d)", - __func__, MAC2STR(addr), status_code, - (int) len); + __func__, MAC2STR(params->addr), + params->status, (int) params->len); return -1; } } @@ -1067,7 +1126,7 @@ atheros_sta_auth(void *priv, const u8 *own_addr, const u8 *addr, u16 seq, if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to auth STA (addr " MACSTR " reason %d)", - __func__, MAC2STR(addr), status_code); + __func__, MAC2STR(params->addr), params->status); } return ret; } @@ -1110,7 +1169,7 @@ atheros_sta_assoc(void *priv, const u8 *own_addr, const u8 *addr, } return ret; } -#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */ static void atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) @@ -1257,7 +1316,7 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, atheros_raw_receive(drv, NULL, (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); #endif /* CONFIG_WPS */ -#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) } else if (os_strncmp(custom, "Manage.assoc_req ", 17) == 0) { /* Format: "Manage.assoc_req <frame len>" | zero padding | * frame */ @@ -1270,20 +1329,20 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, } atheros_raw_receive(drv, NULL, (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); - } else if (os_strncmp(custom, "Manage.auth ", 12) == 0) { + } else if (os_strncmp(custom, "Manage.auth ", 12) == 0) { /* Format: "Manage.auth <frame len>" | zero padding | frame */ int len = atoi(custom + 12); - if (len < 0 || - MGMT_FRAM_TAG_SIZE + len > end - custom) { + if (len < 0 || + MGMT_FRAM_TAG_SIZE + len > end - custom) { wpa_printf(MSG_DEBUG, "Invalid Manage.auth event length %d", len); return; } atheros_raw_receive(drv, NULL, (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); -#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R || CONFIG_FILS */ #ifdef ATHEROS_USE_RAW_RECEIVE - } else if (os_strncmp(custom, "Manage.action ", 14) == 0) { + } else if (os_strncmp(custom, "Manage.action ", 14) == 0) { /* Format: "Manage.assoc_req <frame len>" | zero padding | frame */ int len = atoi(custom + 14); @@ -1299,6 +1358,40 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, } } + +static void send_action_cb_event(struct atheros_driver_data *drv, + char *data, size_t data_len) +{ + union wpa_event_data event; + struct ieee80211_send_action_cb *sa; + const struct ieee80211_hdr *hdr; + u16 fc; + + if (data_len < sizeof(*sa) + 24) { + wpa_printf(MSG_DEBUG, + "athr: Too short event message (data_len=%d sizeof(*sa)=%d)", + (int) data_len, (int) sizeof(*sa)); + wpa_hexdump(MSG_DEBUG, "athr: Short event message", + data, data_len); + return; + } + + sa = (struct ieee80211_send_action_cb *) data; + + hdr = (const struct ieee80211_hdr *) (sa + 1); + fc = le_to_host16(hdr->frame_control); + + os_memset(&event, 0, sizeof(event)); + event.tx_status.type = WLAN_FC_GET_TYPE(fc); + event.tx_status.stype = WLAN_FC_GET_STYPE(fc); + event.tx_status.dst = sa->dst_addr; + event.tx_status.data = (const u8 *) hdr; + event.tx_status.data_len = data_len - sizeof(*sa); + event.tx_status.ack = sa->ack; + wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event); +} + + /* * Handle size of data problem. WEXT only allows data of 256 bytes for custom * events, and p2p data can be much bigger. So the athr driver sends a small @@ -1363,6 +1456,11 @@ static void fetch_pending_big_events(struct atheros_driver_data *drv) &event); continue; } + } else if (frame_type == IEEE80211_EV_P2P_SEND_ACTION_CB) { + wpa_printf(MSG_DEBUG, + "%s: ACTION_CB frame_type=%u len=%zu", + __func__, frame_type, data_len); + send_action_cb_event(drv, (void *) mgmt, data_len); } else { wpa_printf(MSG_DEBUG, "athr: %s unknown type %d", __func__, frame_type); @@ -1376,6 +1474,10 @@ atheros_wireless_event_atheros_custom(struct atheros_driver_data *drv, int opcode, char *buf, int len) { switch (opcode) { + case IEEE80211_EV_P2P_SEND_ACTION_CB: + wpa_printf(MSG_DEBUG, "WEXT: EV_P2P_SEND_ACTION_CB"); + fetch_pending_big_events(drv); + break; case IEEE80211_EV_RX_MGMT: wpa_printf(MSG_DEBUG, "WEXT: EV_RX_MGMT"); fetch_pending_big_events(drv); @@ -1603,6 +1705,27 @@ handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) len - sizeof(struct l2_ethhdr)); } + +static void atheros_read_fils_cap(struct atheros_driver_data *drv) +{ + int fils = 0; + +#ifdef CONFIG_FILS + /* TODO: Would be better to have #ifdef on the IEEE80211_PARAM_* value + * to automatically check this against the driver header files. */ + if (get80211param(drv, IEEE80211_PARAM_ENABLE_FILS, &fils) < 0) { + wpa_printf(MSG_DEBUG, + "%s: Failed to get FILS capability from driver", + __func__); + /* Assume driver does not support FILS */ + fils = 0; + } +#endif /* CONFIG_FILS */ + drv->fils_en = fils; + wpa_printf(MSG_DEBUG, "atheros: fils_en=%d", drv->fils_en); +} + + static void * atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) { @@ -1683,6 +1806,9 @@ atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) if (atheros_wireless_event_init(drv)) goto bad; + /* Read FILS capability from the driver */ + atheros_read_fils_cap(drv); + return drv; bad: atheros_reset_appfilter(drv); @@ -1735,7 +1861,7 @@ atheros_set_ssid(void *priv, const u8 *buf, int len) os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.essid.flags = 1; /* SSID active */ iwr.u.essid.pointer = (caddr_t) buf; - iwr.u.essid.length = len + 1; + iwr.u.essid.length = len; if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s", @@ -1848,7 +1974,7 @@ static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params) } -#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len, int noack, unsigned int freq, @@ -1874,7 +2000,7 @@ static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len, return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm, sizeof(struct ieee80211req_mgmtbuf) + data_len); } -#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */ #ifdef CONFIG_IEEE80211R @@ -2158,11 +2284,11 @@ const struct wpa_driver_ops wpa_driver_atheros_ops = { .set_ap_wps_ie = atheros_set_ap_wps_ie, .set_authmode = atheros_set_authmode, .set_ap = atheros_set_ap, -#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) .sta_assoc = atheros_sta_assoc, .sta_auth = atheros_sta_auth, .send_mlme = atheros_send_mgmt, -#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */ #ifdef CONFIG_IEEE80211R .add_tspec = atheros_add_tspec, .add_sta_node = atheros_add_sta_node, diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c index 2afd7df9637d..8621aa0b0099 100644 --- a/src/drivers/driver_bsd.c +++ b/src/drivers/driver_bsd.c @@ -726,7 +726,7 @@ bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, } -static int +static int bsd_flush(void *priv) { u8 allsta[IEEE80211_ADDR_LEN]; diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c index c7107ba899b0..ac0916e4061f 100644 --- a/src/drivers/driver_common.c +++ b/src/drivers/driver_common.c @@ -1,6 +1,6 @@ /* * Common driver-related functions - * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -35,7 +35,6 @@ const char * event_to_string(enum wpa_event_type event) E2S(ASSOCINFO); E2S(INTERFACE_STATUS); E2S(PMKID_CANDIDATE); - E2S(STKSTART); E2S(TDLS); E2S(FT_RESPONSE); E2S(IBSS_RSN_START); @@ -81,6 +80,13 @@ const char * event_to_string(enum wpa_event_type event) E2S(ACS_CHANNEL_SELECTED); E2S(DFS_CAC_STARTED); E2S(P2P_LO_STOP); + E2S(BEACON_LOSS); + E2S(DFS_PRE_CAC_EXPIRED); + E2S(EXTERNAL_AUTH); + E2S(PORT_AUTHORIZED); + E2S(STATION_OPMODE_CHANGED); + E2S(INTERFACE_MAC_CHANGED); + E2S(WDS_STA_INTERFACE_STATUS); } return "UNKNOWN"; @@ -267,6 +273,19 @@ const char * driver_flag_to_string(u64 flag) DF2S(OFFCHANNEL_SIMULTANEOUS); DF2S(FULL_AP_CLIENT_STATE); DF2S(P2P_LISTEN_OFFLOAD); + DF2S(SUPPORT_FILS); + DF2S(BEACON_RATE_LEGACY); + DF2S(BEACON_RATE_HT); + DF2S(BEACON_RATE_VHT); + DF2S(MGMT_TX_RANDOM_TA); + DF2S(MGMT_TX_RANDOM_TA_CONNECTED); + DF2S(SCHED_SCAN_RELATIVE_RSSI); + DF2S(HE_CAPABILITIES); + DF2S(FILS_SK_OFFLOAD); + DF2S(OCE_STA); + DF2S(OCE_AP); + DF2S(OCE_STA_CFON); + DF2S(MFP_OPTIONAL); } return "UNKNOWN"; #undef DF2S diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c index 517a3bbb5d30..597da335e474 100644 --- a/src/drivers/driver_hostap.c +++ b/src/drivers/driver_hostap.c @@ -741,10 +741,9 @@ static int hostap_set_generic_elem(void *priv, drv->generic_ie = NULL; drv->generic_ie_len = 0; if (elem) { - drv->generic_ie = os_malloc(elem_len); + drv->generic_ie = os_memdup(elem, elem_len); if (drv->generic_ie == NULL) return -1; - os_memcpy(drv->generic_ie, elem, elem_len); drv->generic_ie_len = elem_len; } @@ -768,11 +767,10 @@ static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, drv->wps_ie = NULL; drv->wps_ie_len = 0; if (proberesp) { - drv->wps_ie = os_malloc(wpabuf_len(proberesp)); + drv->wps_ie = os_memdup(wpabuf_head(proberesp), + wpabuf_len(proberesp)); if (drv->wps_ie == NULL) return -1; - os_memcpy(drv->wps_ie, wpabuf_head(proberesp), - wpabuf_len(proberesp)); drv->wps_ie_len = wpabuf_len(proberesp); } @@ -1090,7 +1088,7 @@ static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv, u16 *num_modes, - u16 *flags) + u16 *flags, u8 *dfs) { struct hostapd_hw_modes *mode; int i, clen, rlen; @@ -1105,6 +1103,7 @@ static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv, *num_modes = 1; *flags = 0; + *dfs = 0; mode->mode = HOSTAPD_MODE_IEEE80211B; mode->num_channels = 14; diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c new file mode 100644 index 000000000000..4f6629f5aaa2 --- /dev/null +++ b/src/drivers/driver_macsec_linux.c @@ -0,0 +1,1310 @@ +/* + * Driver interaction with Linux MACsec kernel module + * Copyright (c) 2016, Sabrina Dubroca <sd@queasysnail.net> and Red Hat, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <net/if.h> +#include <netpacket/packet.h> +#include <net/if_arp.h> +#include <net/if.h> +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#include <netlink/route/link.h> +#include <netlink/route/link/macsec.h> +#include <linux/if_macsec.h> +#include <inttypes.h> + +#include "utils/common.h" +#include "utils/eloop.h" +#include "pae/ieee802_1x_kay.h" +#include "driver.h" +#include "driver_wired_common.h" + +#define DRV_PREFIX "macsec_linux: " + +#define UNUSED_SCI 0xffffffffffffffff + +struct cb_arg { + struct macsec_drv_data *drv; + u32 *pn; + int ifindex; + u8 txsa; + u8 rxsa; + u64 rxsci; +}; + +struct macsec_genl_ctx { + struct nl_sock *sk; + int macsec_genl_id; + struct cb_arg cb_arg; +}; + +struct macsec_drv_data { + struct driver_wired_common_data common; + struct rtnl_link *link; + struct nl_cache *link_cache; + struct nl_sock *sk; + struct macsec_genl_ctx ctx; + + struct netlink_data *netlink; + struct nl_handle *nl; + char ifname[IFNAMSIZ + 1]; + int ifi; + int parent_ifi; + + Boolean created_link; + + Boolean controlled_port_enabled; + Boolean controlled_port_enabled_set; + + Boolean protect_frames; + Boolean protect_frames_set; + + Boolean encrypt; + Boolean encrypt_set; + + Boolean replay_protect; + Boolean replay_protect_set; + + u32 replay_window; + + u8 encoding_sa; + Boolean encoding_sa_set; +}; + + +static int dump_callback(struct nl_msg *msg, void *argp); + + +static struct nl_msg * msg_prepare(enum macsec_nl_commands cmd, + const struct macsec_genl_ctx *ctx, + unsigned int ifindex) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) { + wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc message"); + return NULL; + } + + if (!genlmsg_put(msg, 0, 0, ctx->macsec_genl_id, 0, 0, cmd, 0)) { + wpa_printf(MSG_ERROR, DRV_PREFIX "failed to put header"); + goto nla_put_failure; + } + + NLA_PUT_U32(msg, MACSEC_ATTR_IFINDEX, ifindex); + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + + +static int nla_put_rxsc_config(struct nl_msg *msg, u64 sci) +{ + struct nlattr *nest = nla_nest_start(msg, MACSEC_ATTR_RXSC_CONFIG); + + if (!nest) + return -1; + + NLA_PUT_U64(msg, MACSEC_RXSC_ATTR_SCI, sci); + + nla_nest_end(msg, nest); + + return 0; + +nla_put_failure: + return -1; +} + + +static int init_genl_ctx(struct macsec_drv_data *drv) +{ + struct macsec_genl_ctx *ctx = &drv->ctx; + + ctx->sk = nl_socket_alloc(); + if (!ctx->sk) { + wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket"); + return -1; + } + + if (genl_connect(ctx->sk) < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "connection to genl socket failed"); + goto out_free; + } + + ctx->macsec_genl_id = genl_ctrl_resolve(ctx->sk, "macsec"); + if (ctx->macsec_genl_id < 0) { + wpa_printf(MSG_ERROR, DRV_PREFIX "genl resolve failed"); + goto out_free; + } + + memset(&ctx->cb_arg, 0, sizeof(ctx->cb_arg)); + ctx->cb_arg.drv = drv; + + nl_socket_modify_cb(ctx->sk, NL_CB_VALID, NL_CB_CUSTOM, dump_callback, + &ctx->cb_arg); + + return 0; + +out_free: + nl_socket_free(ctx->sk); + ctx->sk = NULL; + return -1; +} + + +static int try_commit(struct macsec_drv_data *drv) +{ + int err; + + if (!drv->sk) + return 0; + + if (!drv->link) + return 0; + + if (drv->controlled_port_enabled_set) { + struct rtnl_link *change = rtnl_link_alloc(); + + if (!change) + return -1; + + rtnl_link_set_name(change, drv->ifname); + + if (drv->controlled_port_enabled) + rtnl_link_set_flags(change, IFF_UP); + else + rtnl_link_unset_flags(change, IFF_UP); + + err = rtnl_link_change(drv->sk, change, change, 0); + if (err < 0) + return err; + + rtnl_link_put(change); + + drv->controlled_port_enabled_set = FALSE; + } + + if (drv->protect_frames_set) + rtnl_link_macsec_set_protect(drv->link, drv->protect_frames); + + if (drv->encrypt_set) + rtnl_link_macsec_set_encrypt(drv->link, drv->encrypt); + + if (drv->replay_protect_set) { + rtnl_link_macsec_set_replay_protect(drv->link, + drv->replay_protect); + if (drv->replay_protect) + rtnl_link_macsec_set_window(drv->link, + drv->replay_window); + } + + if (drv->encoding_sa_set) + rtnl_link_macsec_set_encoding_sa(drv->link, drv->encoding_sa); + + err = rtnl_link_add(drv->sk, drv->link, 0); + if (err < 0) + return err; + + drv->protect_frames_set = FALSE; + drv->encrypt_set = FALSE; + drv->replay_protect_set = FALSE; + + return 0; +} + + +static void macsec_drv_wpa_deinit(void *priv) +{ + struct macsec_drv_data *drv = priv; + + driver_wired_deinit_common(&drv->common); + os_free(drv); +} + + +static int macsec_check_macsec(void) +{ + struct nl_sock *sk; + int err = -1; + + sk = nl_socket_alloc(); + if (!sk) { + wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket"); + return -1; + } + + if (genl_connect(sk) < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "connection to genl socket failed"); + goto out_free; + } + + if (genl_ctrl_resolve(sk, "macsec") < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "genl resolve failed - macsec kernel module not present?"); + goto out_free; + } + + err = 0; + +out_free: + nl_socket_free(sk); + return err; +} + + +static void * macsec_drv_wpa_init(void *ctx, const char *ifname) +{ + struct macsec_drv_data *drv; + + if (macsec_check_macsec() < 0) + return NULL; + + drv = os_zalloc(sizeof(*drv)); + if (!drv) + return NULL; + + if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) { + os_free(drv); + return NULL; + } + + return drv; +} + + +static int macsec_drv_macsec_init(void *priv, struct macsec_init_params *params) +{ + struct macsec_drv_data *drv = priv; + int err; + + wpa_printf(MSG_DEBUG, "%s", __func__); + + drv->sk = nl_socket_alloc(); + if (!drv->sk) + return -1; + + err = nl_connect(drv->sk, NETLINK_ROUTE); + if (err < 0) { + wpa_printf(MSG_ERROR, DRV_PREFIX + "Unable to connect NETLINK_ROUTE socket: %s", + strerror(errno)); + goto sock; + } + + err = rtnl_link_alloc_cache(drv->sk, AF_UNSPEC, &drv->link_cache); + if (err < 0) { + wpa_printf(MSG_ERROR, DRV_PREFIX "Unable to get link cache: %s", + strerror(errno)); + goto sock; + } + + drv->parent_ifi = rtnl_link_name2i(drv->link_cache, drv->common.ifname); + if (drv->parent_ifi == 0) { + wpa_printf(MSG_ERROR, DRV_PREFIX + "couldn't find ifindex for interface %s", + drv->common.ifname); + goto cache; + } + + err = init_genl_ctx(drv); + if (err < 0) + goto cache; + + return 0; + +cache: + nl_cache_free(drv->link_cache); + drv->link_cache = NULL; +sock: + nl_socket_free(drv->sk); + drv->sk = NULL; + return -1; +} + + +static int macsec_drv_macsec_deinit(void *priv) +{ + struct macsec_drv_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s", __func__); + + if (drv->sk) + nl_socket_free(drv->sk); + drv->sk = NULL; + + if (drv->link_cache) + nl_cache_free(drv->link_cache); + drv->link_cache = NULL; + + if (drv->ctx.sk) + nl_socket_free(drv->ctx.sk); + + return 0; +} + + +static int macsec_drv_get_capability(void *priv, enum macsec_cap *cap) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + + *cap = MACSEC_CAP_INTEG_AND_CONF; + + return 0; +} + + +/** + * macsec_drv_enable_protect_frames - Set protect frames status + * @priv: Private driver interface data + * @enabled: TRUE = protect frames enabled + * FALSE = protect frames disabled + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_enable_protect_frames(void *priv, Boolean enabled) +{ + struct macsec_drv_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE"); + + drv->protect_frames_set = TRUE; + drv->protect_frames = enabled; + + return try_commit(drv); +} + + +/** + * macsec_drv_enable_encrypt - Set protect frames status + * @priv: Private driver interface data + * @enabled: TRUE = protect frames enabled + * FALSE = protect frames disabled + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_enable_encrypt(void *priv, Boolean enabled) +{ + struct macsec_drv_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE"); + + drv->encrypt_set = TRUE; + drv->encrypt = enabled; + + return try_commit(drv); +} + + +/** + * macsec_drv_set_replay_protect - Set replay protect status and window size + * @priv: Private driver interface data + * @enabled: TRUE = replay protect enabled + * FALSE = replay protect disabled + * @window: replay window size, valid only when replay protect enabled + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_set_replay_protect(void *priv, Boolean enabled, + u32 window) +{ + struct macsec_drv_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s -> %s, %u", __func__, + enabled ? "TRUE" : "FALSE", window); + + drv->replay_protect_set = TRUE; + drv->replay_protect = enabled; + if (enabled) + drv->replay_window = window; + + return try_commit(drv); +} + + +/** + * macsec_drv_set_current_cipher_suite - Set current cipher suite + * @priv: Private driver interface data + * @cs: EUI64 identifier + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_set_current_cipher_suite(void *priv, u64 cs) +{ + wpa_printf(MSG_DEBUG, "%s -> %016" PRIx64, __func__, cs); + return 0; +} + + +/** + * macsec_drv_enable_controlled_port - Set controlled port status + * @priv: Private driver interface data + * @enabled: TRUE = controlled port enabled + * FALSE = controlled port disabled + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_enable_controlled_port(void *priv, Boolean enabled) +{ + struct macsec_drv_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE"); + + drv->controlled_port_enabled = enabled; + drv->controlled_port_enabled_set = TRUE; + + return try_commit(drv); +} + + +static struct nla_policy sa_policy[MACSEC_SA_ATTR_MAX + 1] = { + [MACSEC_SA_ATTR_AN] = { .type = NLA_U8 }, + [MACSEC_SA_ATTR_ACTIVE] = { .type = NLA_U8 }, + [MACSEC_SA_ATTR_PN] = { .type = NLA_U32 }, + [MACSEC_SA_ATTR_KEYID] = { .type = NLA_BINARY }, +}; + +static struct nla_policy sc_policy[MACSEC_RXSC_ATTR_MAX + 1] = { + [MACSEC_RXSC_ATTR_SCI] = { .type = NLA_U64 }, + [MACSEC_RXSC_ATTR_ACTIVE] = { .type = NLA_U8 }, + [MACSEC_RXSC_ATTR_SA_LIST] = { .type = NLA_NESTED }, +}; + +static struct nla_policy main_policy[MACSEC_ATTR_MAX + 1] = { + [MACSEC_ATTR_IFINDEX] = { .type = NLA_U32 }, + [MACSEC_ATTR_SECY] = { .type = NLA_NESTED }, + [MACSEC_ATTR_TXSA_LIST] = { .type = NLA_NESTED }, + [MACSEC_ATTR_RXSC_LIST] = { .type = NLA_NESTED }, +}; + +static int dump_callback(struct nl_msg *msg, void *argp) +{ + struct nlmsghdr *ret_hdr = nlmsg_hdr(msg); + struct nlattr *tb_msg[MACSEC_ATTR_MAX + 1]; + struct cb_arg *arg = (struct cb_arg *) argp; + struct genlmsghdr *gnlh = (struct genlmsghdr *) nlmsg_data(ret_hdr); + int err; + + if (ret_hdr->nlmsg_type != arg->drv->ctx.macsec_genl_id) + return 0; + + err = nla_parse(tb_msg, MACSEC_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), main_policy); + if (err < 0) + return 0; + + if (!tb_msg[MACSEC_ATTR_IFINDEX]) + return 0; + + if (nla_get_u32(tb_msg[MACSEC_ATTR_IFINDEX]) != (u32) arg->ifindex) + return 0; + + if (arg->txsa < 4 && !tb_msg[MACSEC_ATTR_TXSA_LIST]) { + return 0; + } else if (arg->txsa < 4) { + struct nlattr *nla; + int rem; + + nla_for_each_nested(nla, tb_msg[MACSEC_ATTR_TXSA_LIST], rem) { + struct nlattr *tb[MACSEC_SA_ATTR_MAX + 1]; + + err = nla_parse_nested(tb, MACSEC_SA_ATTR_MAX, nla, + sa_policy); + if (err < 0) + continue; + if (!tb[MACSEC_SA_ATTR_AN]) + continue; + if (nla_get_u8(tb[MACSEC_SA_ATTR_AN]) != arg->txsa) + continue; + if (!tb[MACSEC_SA_ATTR_PN]) + return 0; + *arg->pn = nla_get_u32(tb[MACSEC_SA_ATTR_PN]); + return 0; + } + + return 0; + } + + if (arg->rxsci == UNUSED_SCI) + return 0; + + if (tb_msg[MACSEC_ATTR_RXSC_LIST]) { + struct nlattr *nla; + int rem; + + nla_for_each_nested(nla, tb_msg[MACSEC_ATTR_RXSC_LIST], rem) { + struct nlattr *tb[MACSEC_RXSC_ATTR_MAX + 1]; + + err = nla_parse_nested(tb, MACSEC_RXSC_ATTR_MAX, nla, + sc_policy); + if (err < 0) + return 0; + if (!tb[MACSEC_RXSC_ATTR_SCI]) + continue; + if (nla_get_u64(tb[MACSEC_RXSC_ATTR_SCI]) != arg->rxsci) + continue; + if (!tb[MACSEC_RXSC_ATTR_SA_LIST]) + return 0; + + nla_for_each_nested(nla, tb[MACSEC_RXSC_ATTR_SA_LIST], + rem) { + struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1]; + + err = nla_parse_nested(tb_sa, + MACSEC_SA_ATTR_MAX, nla, + sa_policy); + if (err < 0) + continue; + if (!tb_sa[MACSEC_SA_ATTR_AN]) + continue; + if (nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]) != + arg->rxsa) + continue; + if (!tb_sa[MACSEC_SA_ATTR_PN]) + return 0; + *arg->pn = + nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); + + return 0; + } + + return 0; + } + + return 0; + } + + return 0; +} + + +static int nl_send_recv(struct nl_sock *sk, struct nl_msg *msg) +{ + int ret; + + ret = nl_send_auto_complete(sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to send: %d (%s)", + __func__, ret, nl_geterror(-ret)); + return ret; + } + + ret = nl_recvmsgs_default(sk); + if (ret < 0) { + wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to recv: %d (%s)", + __func__, ret, nl_geterror(-ret)); + } + + return ret; +} + + +static int do_dump(struct macsec_drv_data *drv, u8 txsa, u64 rxsci, u8 rxsa, + u32 *pn) +{ + struct macsec_genl_ctx *ctx = &drv->ctx; + struct nl_msg *msg; + int ret = 1; + + ctx->cb_arg.ifindex = drv->ifi; + ctx->cb_arg.rxsci = rxsci; + ctx->cb_arg.rxsa = rxsa; + ctx->cb_arg.txsa = txsa; + ctx->cb_arg.pn = pn; + + msg = nlmsg_alloc(); + if (!msg) { + wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to alloc message", + __func__); + return 1; + } + + if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, ctx->macsec_genl_id, 0, + NLM_F_DUMP, MACSEC_CMD_GET_TXSC, 0)) { + wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to put header", + __func__); + goto out_free_msg; + } + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) + wpa_printf(MSG_ERROR, + DRV_PREFIX "failed to communicate: %d (%s)", + ret, nl_geterror(-ret)); + + ctx->cb_arg.pn = NULL; + +out_free_msg: + nlmsg_free(msg); + return ret; +} + + +/** + * macsec_drv_get_receive_lowest_pn - Get receive lowest PN + * @priv: Private driver interface data + * @sa: secure association + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_get_receive_lowest_pn(void *priv, struct receive_sa *sa) +{ + struct macsec_drv_data *drv = priv; + int err; + + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s", __func__); + + err = do_dump(drv, 0xff, mka_sci_u64(&sa->sc->sci), sa->an, + &sa->lowest_pn); + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: result %d", __func__, + sa->lowest_pn); + + return err; +} + + +/** + * macsec_drv_get_transmit_next_pn - Get transmit next PN + * @priv: Private driver interface data + * @sa: secure association + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_get_transmit_next_pn(void *priv, struct transmit_sa *sa) +{ + struct macsec_drv_data *drv = priv; + int err; + + wpa_printf(MSG_DEBUG, "%s", __func__); + + err = do_dump(drv, sa->an, UNUSED_SCI, 0xff, &sa->next_pn); + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: err %d result %d", __func__, err, + sa->next_pn); + return err; +} + + +/** + * macsec_drv_set_transmit_next_pn - Set transmit next pn + * @priv: Private driver interface data + * @sa: secure association + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_set_transmit_next_pn(void *priv, struct transmit_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + struct nl_msg *msg; + struct nlattr *nest; + int ret = -1; + + wpa_printf(MSG_DEBUG, "%s -> %d: %d", __func__, sa->an, sa->next_pn); + + msg = msg_prepare(MACSEC_CMD_UPD_TXSA, ctx, drv->ifi); + if (!msg) + return ret; + + nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG); + if (!nest) + goto nla_put_failure; + + NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an); + NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn); + + nla_nest_end(msg, nest); + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "failed to communicate: %d (%s)", + ret, nl_geterror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +#define SCISTR MACSTR "::%hx" +#define SCI2STR(addr, port) MAC2STR(addr), htons(port) + +/** + * macsec_drv_create_receive_sc - Create secure channel for receiving + * @priv: Private driver interface data + * @sc: secure channel + * @sci_addr: secure channel identifier - address + * @sci_port: secure channel identifier - port + * @conf_offset: confidentiality offset (0, 30, or 50) + * @validation: frame validation policy (0 = Disabled, 1 = Checked, + * 2 = Strict) + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_create_receive_sc(void *priv, struct receive_sc *sc, + unsigned int conf_offset, + int validation) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + struct nl_msg *msg; + int ret = -1; + + wpa_printf(MSG_DEBUG, "%s -> " SCISTR, __func__, + SCI2STR(sc->sci.addr, sc->sci.port)); + + msg = msg_prepare(MACSEC_CMD_ADD_RXSC, ctx, drv->ifi); + if (!msg) + return ret; + + if (nla_put_rxsc_config(msg, mka_sci_u64(&sc->sci))) + goto nla_put_failure; + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "%s: failed to communicate: %d (%s)", + __func__, ret, nl_geterror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +/** + * macsec_drv_delete_receive_sc - Delete secure connection for receiving + * @priv: private driver interface data from init() + * @sc: secure channel + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_delete_receive_sc(void *priv, struct receive_sc *sc) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + struct nl_msg *msg; + int ret = -1; + + wpa_printf(MSG_DEBUG, "%s -> " SCISTR, __func__, + SCI2STR(sc->sci.addr, sc->sci.port)); + + msg = msg_prepare(MACSEC_CMD_DEL_RXSC, ctx, drv->ifi); + if (!msg) + return ret; + + if (nla_put_rxsc_config(msg, mka_sci_u64(&sc->sci))) + goto nla_put_failure; + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "%s: failed to communicate: %d (%s)", + __func__, ret, nl_geterror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +/** + * macsec_drv_create_receive_sa - Create secure association for receive + * @priv: private driver interface data from init() + * @sa: secure association + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_create_receive_sa(void *priv, struct receive_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + struct nl_msg *msg; + struct nlattr *nest; + int ret = -1; + + wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an, + SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); + + msg = msg_prepare(MACSEC_CMD_ADD_RXSA, ctx, drv->ifi); + if (!msg) + return ret; + + if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci))) + goto nla_put_failure; + + nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG); + if (!nest) + goto nla_put_failure; + + NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an); + NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, sa->enable_receive); + NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn); + NLA_PUT(msg, MACSEC_SA_ATTR_KEYID, sizeof(sa->pkey->key_identifier), + &sa->pkey->key_identifier); + NLA_PUT(msg, MACSEC_SA_ATTR_KEY, sa->pkey->key_len, sa->pkey->key); + + nla_nest_end(msg, nest); + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "%s: failed to communicate: %d (%s)", + __func__, ret, nl_geterror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +/** + * macsec_drv_delete_receive_sa - Delete secure association for receive + * @priv: private driver interface data from init() + * @sa: secure association + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_delete_receive_sa(void *priv, struct receive_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + struct nl_msg *msg; + struct nlattr *nest; + int ret = -1; + + wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an, + SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); + + msg = msg_prepare(MACSEC_CMD_DEL_RXSA, ctx, drv->ifi); + if (!msg) + return ret; + + if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci))) + goto nla_put_failure; + + nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG); + if (!nest) + goto nla_put_failure; + + NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an); + + nla_nest_end(msg, nest); + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "%s: failed to communicate: %d (%s)", + __func__, ret, nl_geterror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int set_active_rx_sa(const struct macsec_genl_ctx *ctx, int ifindex, + u64 sci, unsigned char an, Boolean state) +{ + struct nl_msg *msg; + struct nlattr *nest; + int ret = -1; + + msg = msg_prepare(MACSEC_CMD_UPD_RXSA, ctx, ifindex); + if (!msg) + return ret; + + if (nla_put_rxsc_config(msg, sci)) + goto nla_put_failure; + + nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG); + if (!nest) + goto nla_put_failure; + + NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, an); + NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, !!state); + + nla_nest_end(msg, nest); + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) + wpa_printf(MSG_ERROR, + DRV_PREFIX "%s: failed to communicate: %d (%s)", + __func__, ret, nl_geterror(-ret)); + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +/** + * macsec_drv_enable_receive_sa - Enable the SA for receive + * @priv: private driver interface data from init() + * @sa: secure association + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_enable_receive_sa(void *priv, struct receive_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + + wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an, + SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); + + return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci), + sa->an, TRUE); +} + + +/** + * macsec_drv_disable_receive_sa - Disable SA for receive + * @priv: private driver interface data from init() + * @sa: secure association + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_disable_receive_sa(void *priv, struct receive_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + + wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an, + SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); + + return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci), + sa->an, FALSE); +} + + +static struct rtnl_link * lookup_sc(struct nl_cache *cache, int parent, u64 sci) +{ + struct rtnl_link *needle; + void *match; + + needle = rtnl_link_macsec_alloc(); + if (!needle) + return NULL; + + rtnl_link_set_link(needle, parent); + rtnl_link_macsec_set_sci(needle, sci); + + match = nl_cache_find(cache, (struct nl_object *) needle); + rtnl_link_put(needle); + + return (struct rtnl_link *) match; +} + + +/** + * macsec_drv_create_transmit_sc - Create secure connection for transmit + * @priv: private driver interface data from init() + * @sc: secure channel + * @conf_offset: confidentiality offset + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_create_transmit_sc( + void *priv, struct transmit_sc *sc, + unsigned int conf_offset) +{ + struct macsec_drv_data *drv = priv; + struct rtnl_link *link; + char *ifname; + u64 sci; + int err; + + wpa_printf(MSG_DEBUG, "%s", __func__); + + if (!drv->sk) { + wpa_printf(MSG_ERROR, DRV_PREFIX "NULL rtnl socket"); + return -1; + } + + link = rtnl_link_macsec_alloc(); + if (!link) { + wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link"); + return -1; + } + + rtnl_link_set_link(link, drv->parent_ifi); + + sci = mka_sci_u64(&sc->sci); + rtnl_link_macsec_set_sci(link, sci); + + drv->created_link = TRUE; + + err = rtnl_link_add(drv->sk, link, NLM_F_CREATE); + if (err == -NLE_BUSY) { + wpa_printf(MSG_INFO, + DRV_PREFIX "link already exists, using it"); + drv->created_link = FALSE; + } else if (err < 0) { + rtnl_link_put(link); + wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't create link: err %d", + err); + return err; + } + + rtnl_link_put(link); + + nl_cache_refill(drv->sk, drv->link_cache); + link = lookup_sc(drv->link_cache, drv->parent_ifi, sci); + if (!link) { + wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't find link"); + return -1; + } + + drv->ifi = rtnl_link_get_ifindex(link); + ifname = rtnl_link_get_name(link); + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + rtnl_link_put(link); + + drv->link = rtnl_link_macsec_alloc(); + if (!drv->link) { + wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link"); + return -1; + } + + rtnl_link_set_name(drv->link, drv->ifname); + + /* In case some settings have already been done but we couldn't apply + * them. */ + return try_commit(drv); +} + + +/** + * macsec_drv_delete_transmit_sc - Delete secure connection for transmit + * @priv: private driver interface data from init() + * @sc: secure channel + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_delete_transmit_sc(void *priv, struct transmit_sc *sc) +{ + struct macsec_drv_data *drv = priv; + int err; + + wpa_printf(MSG_DEBUG, "%s", __func__); + + if (!drv->sk) + return 0; + + if (!drv->created_link) { + rtnl_link_put(drv->link); + drv->link = NULL; + wpa_printf(MSG_DEBUG, DRV_PREFIX + "we didn't create the link, leave it alone"); + return 0; + } + + err = rtnl_link_delete(drv->sk, drv->link); + if (err < 0) + wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't delete link"); + rtnl_link_put(drv->link); + drv->link = NULL; + + return err; +} + + +/** + * macsec_drv_create_transmit_sa - Create secure association for transmit + * @priv: private driver interface data from init() + * @sa: secure association + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_create_transmit_sa(void *priv, struct transmit_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + struct nl_msg *msg; + struct nlattr *nest; + int ret = -1; + + wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an); + + msg = msg_prepare(MACSEC_CMD_ADD_TXSA, ctx, drv->ifi); + if (!msg) + return ret; + + nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG); + if (!nest) + goto nla_put_failure; + + NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an); + NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn); + NLA_PUT(msg, MACSEC_SA_ATTR_KEYID, sizeof(sa->pkey->key_identifier), + &sa->pkey->key_identifier); + NLA_PUT(msg, MACSEC_SA_ATTR_KEY, sa->pkey->key_len, sa->pkey->key); + NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, sa->enable_transmit); + + nla_nest_end(msg, nest); + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "%s: failed to communicate: %d (%s)", + __func__, ret, nl_geterror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +/** + * macsec_drv_delete_transmit_sa - Delete secure association for transmit + * @priv: private driver interface data from init() + * @sa: secure association + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_delete_transmit_sa(void *priv, struct transmit_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + struct nl_msg *msg; + struct nlattr *nest; + int ret = -1; + + wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an); + + msg = msg_prepare(MACSEC_CMD_DEL_TXSA, ctx, drv->ifi); + if (!msg) + return ret; + + nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG); + if (!nest) + goto nla_put_failure; + + NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an); + + nla_nest_end(msg, nest); + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "%s: failed to communicate: %d (%s)", + __func__, ret, nl_geterror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int set_active_tx_sa(const struct macsec_genl_ctx *ctx, int ifindex, + unsigned char an, Boolean state) +{ + struct nl_msg *msg; + struct nlattr *nest; + int ret = -1; + + msg = msg_prepare(MACSEC_CMD_UPD_TXSA, ctx, ifindex); + if (!msg) + return ret; + + nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG); + if (!nest) + goto nla_put_failure; + + NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, an); + NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, !!state); + + nla_nest_end(msg, nest); + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "%s: failed to communicate: %d (%s)", + __func__, ret, nl_geterror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +/** + * macsec_drv_enable_transmit_sa - Enable SA for transmit + * @priv: private driver interface data from init() + * @sa: secure association + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_enable_transmit_sa(void *priv, struct transmit_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + int ret; + + wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an); + + ret = set_active_tx_sa(ctx, drv->ifi, sa->an, TRUE); + if (ret < 0) { + wpa_printf(MSG_ERROR, DRV_PREFIX "failed to enable txsa"); + return ret; + } + + drv->encoding_sa_set = TRUE; + drv->encoding_sa = sa->an; + + return try_commit(drv); +} + + +/** + * macsec_drv_disable_transmit_sa - Disable SA for transmit + * @priv: private driver interface data from init() + * @sa: secure association + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_disable_transmit_sa(void *priv, struct transmit_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + + wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an); + + return set_active_tx_sa(ctx, drv->ifi, sa->an, FALSE); +} + + +const struct wpa_driver_ops wpa_driver_macsec_linux_ops = { + .name = "macsec_linux", + .desc = "MACsec Ethernet driver for Linux", + .get_ssid = driver_wired_get_ssid, + .get_bssid = driver_wired_get_bssid, + .get_capa = driver_wired_get_capa, + .init = macsec_drv_wpa_init, + .deinit = macsec_drv_wpa_deinit, + + .macsec_init = macsec_drv_macsec_init, + .macsec_deinit = macsec_drv_macsec_deinit, + .macsec_get_capability = macsec_drv_get_capability, + .enable_protect_frames = macsec_drv_enable_protect_frames, + .enable_encrypt = macsec_drv_enable_encrypt, + .set_replay_protect = macsec_drv_set_replay_protect, + .set_current_cipher_suite = macsec_drv_set_current_cipher_suite, + .enable_controlled_port = macsec_drv_enable_controlled_port, + .get_receive_lowest_pn = macsec_drv_get_receive_lowest_pn, + .get_transmit_next_pn = macsec_drv_get_transmit_next_pn, + .set_transmit_next_pn = macsec_drv_set_transmit_next_pn, + .create_receive_sc = macsec_drv_create_receive_sc, + .delete_receive_sc = macsec_drv_delete_receive_sc, + .create_receive_sa = macsec_drv_create_receive_sa, + .delete_receive_sa = macsec_drv_delete_receive_sa, + .enable_receive_sa = macsec_drv_enable_receive_sa, + .disable_receive_sa = macsec_drv_disable_receive_sa, + .create_transmit_sc = macsec_drv_create_transmit_sc, + .delete_transmit_sc = macsec_drv_delete_transmit_sc, + .create_transmit_sa = macsec_drv_create_transmit_sa, + .delete_transmit_sa = macsec_drv_delete_transmit_sa, + .enable_transmit_sa = macsec_drv_enable_transmit_sa, + .disable_transmit_sa = macsec_drv_disable_transmit_sa, +}; diff --git a/src/drivers/driver_macsec_qca.c b/src/drivers/driver_macsec_qca.c index 826d3cc62133..8372393f26c2 100644 --- a/src/drivers/driver_macsec_qca.c +++ b/src/drivers/driver_macsec_qca.c @@ -29,7 +29,9 @@ #include "utils/eloop.h" #include "common/defs.h" #include "common/ieee802_1x_defs.h" +#include "pae/ieee802_1x_kay.h" #include "driver.h" +#include "driver_wired_common.h" #include "nss_macsec_secy.h" #include "nss_macsec_secy_rx.h" @@ -37,6 +39,9 @@ #define MAXSC 16 +#define SAK_128_LEN 16 +#define SAK_256_LEN 32 + /* TCI field definition */ #define TCI_ES 0x40 #define TCI_SC 0x20 @@ -52,17 +57,14 @@ #pragma pack(pop) #endif /* _MSC_VER */ -static const u8 pae_group_addr[ETH_ALEN] = -{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; +struct channel_map { + struct ieee802_1x_mka_sci sci; +}; struct macsec_qca_data { - char ifname[IFNAMSIZ + 1]; - u32 secy_id; - void *ctx; + struct driver_wired_common_data common; - int sock; /* raw packet socket for driver access */ - int pf_sock; - int membership, multi, iff_allmulti, iff_up; + u32 secy_id; /* shadow */ Boolean always_include_sci; @@ -71,192 +73,10 @@ struct macsec_qca_data { Boolean protect_frames; Boolean replay_protect; u32 replay_window; -}; - - -static int macsec_qca_multicast_membership(int sock, int ifindex, - const u8 *addr, int add) -{ -#ifdef __linux__ - struct packet_mreq mreq; - - if (sock < 0) - return -1; - - os_memset(&mreq, 0, sizeof(mreq)); - mreq.mr_ifindex = ifindex; - mreq.mr_type = PACKET_MR_MULTICAST; - mreq.mr_alen = ETH_ALEN; - os_memcpy(mreq.mr_address, addr, ETH_ALEN); - - if (setsockopt(sock, SOL_PACKET, - add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, - &mreq, sizeof(mreq)) < 0) { - wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno)); - return -1; - } - return 0; -#else /* __linux__ */ - return -1; -#endif /* __linux__ */ -} - - -static int macsec_qca_get_ssid(void *priv, u8 *ssid) -{ - ssid[0] = 0; - return 0; -} - -static int macsec_qca_get_bssid(void *priv, u8 *bssid) -{ - /* Report PAE group address as the "BSSID" for macsec connection. */ - os_memcpy(bssid, pae_group_addr, ETH_ALEN); - return 0; -} - - -static int macsec_qca_get_capa(void *priv, struct wpa_driver_capa *capa) -{ - os_memset(capa, 0, sizeof(*capa)); - capa->flags = WPA_DRIVER_FLAGS_WIRED; - return 0; -} - - -static int macsec_qca_get_ifflags(const char *ifname, int *flags) -{ - struct ifreq ifr; - int s; - - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) { - wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); - return -1; - } - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s", - strerror(errno)); - close(s); - return -1; - } - close(s); - *flags = ifr.ifr_flags & 0xffff; - return 0; -} - - -static int macsec_qca_set_ifflags(const char *ifname, int flags) -{ - struct ifreq ifr; - int s; - - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) { - wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); - return -1; - } - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_flags = flags & 0xffff; - if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s", - strerror(errno)); - close(s); - return -1; - } - close(s); - return 0; -} - - -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) -static int macsec_qca_get_ifstatus(const char *ifname, int *status) -{ - struct ifmediareq ifmr; - int s; - - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) { - wpa_print(MSG_ERROR, "socket: %s", strerror(errno)); - return -1; - } - - os_memset(&ifmr, 0, sizeof(ifmr)); - os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ); - if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s", - strerror(errno)); - close(s); - return -1; - } - close(s); - *status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) == - (IFM_ACTIVE | IFM_AVALID); - - return 0; -} -#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ - - -static int macsec_qca_multi(const char *ifname, const u8 *addr, int add) -{ - struct ifreq ifr; - int s; - -#ifdef __sun__ - return -1; -#endif /* __sun__ */ - - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) { - wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); - return -1; - } - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); -#ifdef __linux__ - ifr.ifr_hwaddr.sa_family = AF_UNSPEC; - os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); -#endif /* __linux__ */ -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) - { - struct sockaddr_dl *dlp; - dlp = (struct sockaddr_dl *) &ifr.ifr_addr; - dlp->sdl_len = sizeof(struct sockaddr_dl); - dlp->sdl_family = AF_LINK; - dlp->sdl_index = 0; - dlp->sdl_nlen = 0; - dlp->sdl_alen = ETH_ALEN; - dlp->sdl_slen = 0; - os_memcpy(LLADDR(dlp), addr, ETH_ALEN); - } -#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ -#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) - { - struct sockaddr *sap; - sap = (struct sockaddr *) &ifr.ifr_addr; - sap->sa_len = sizeof(struct sockaddr); - sap->sa_family = AF_UNSPEC; - os_memcpy(sap->sa_data, addr, ETH_ALEN); - } -#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */ - - if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s", - strerror(errno)); - close(s); - return -1; - } - close(s); - return 0; -} + struct channel_map receive_channel_map[MAXSC]; + struct channel_map transmit_channel_map[MAXSC]; +}; static void __macsec_drv_init(struct macsec_qca_data *drv) @@ -309,76 +129,23 @@ static void __macsec_drv_deinit(struct macsec_qca_data *drv) static void * macsec_qca_init(void *ctx, const char *ifname) { struct macsec_qca_data *drv; - int flags; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->ctx = ctx; /* Board specific settings */ - if (os_memcmp("eth2", drv->ifname, 4) == 0) + if (os_memcmp("eth2", ifname, 4) == 0) drv->secy_id = 1; - else if (os_memcmp("eth3", drv->ifname, 4) == 0) + else if (os_memcmp("eth3", ifname, 4) == 0) drv->secy_id = 2; else drv->secy_id = -1; -#ifdef __linux__ - drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0); - if (drv->pf_sock < 0) - wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno)); -#else /* __linux__ */ - drv->pf_sock = -1; -#endif /* __linux__ */ - - if (macsec_qca_get_ifflags(ifname, &flags) == 0 && - !(flags & IFF_UP) && - macsec_qca_set_ifflags(ifname, flags | IFF_UP) == 0) { - drv->iff_up = 1; - } - - if (macsec_qca_multicast_membership(drv->pf_sock, - if_nametoindex(drv->ifname), - pae_group_addr, 1) == 0) { - wpa_printf(MSG_DEBUG, - "%s: Added multicast membership with packet socket", - __func__); - drv->membership = 1; - } else if (macsec_qca_multi(ifname, pae_group_addr, 1) == 0) { - wpa_printf(MSG_DEBUG, - "%s: Added multicast membership with SIOCADDMULTI", - __func__); - drv->multi = 1; - } else if (macsec_qca_get_ifflags(ifname, &flags) < 0) { - wpa_printf(MSG_INFO, "%s: Could not get interface flags", - __func__); - os_free(drv); - return NULL; - } else if (flags & IFF_ALLMULTI) { - wpa_printf(MSG_DEBUG, - "%s: Interface is already configured for multicast", - __func__); - } else if (macsec_qca_set_ifflags(ifname, flags | IFF_ALLMULTI) < 0) { - wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", - __func__); + if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) { os_free(drv); return NULL; - } else { - wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", __func__); - drv->iff_allmulti = 1; } -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) - { - int status; - wpa_printf(MSG_DEBUG, "%s: waiting for link to become active", - __func__); - while (macsec_qca_get_ifstatus(ifname, &status) == 0 && - status == 0) - sleep(1); - } -#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ return drv; } @@ -387,42 +154,8 @@ static void * macsec_qca_init(void *ctx, const char *ifname) static void macsec_qca_deinit(void *priv) { struct macsec_qca_data *drv = priv; - int flags; - - if (drv->membership && - macsec_qca_multicast_membership(drv->pf_sock, - if_nametoindex(drv->ifname), - pae_group_addr, 0) < 0) { - wpa_printf(MSG_DEBUG, - "%s: Failed to remove PAE multicast group (PACKET)", - __func__); - } - - if (drv->multi && - macsec_qca_multi(drv->ifname, pae_group_addr, 0) < 0) { - wpa_printf(MSG_DEBUG, - "%s: Failed to remove PAE multicast group (SIOCDELMULTI)", - __func__); - } - - if (drv->iff_allmulti && - (macsec_qca_get_ifflags(drv->ifname, &flags) < 0 || - macsec_qca_set_ifflags(drv->ifname, flags & ~IFF_ALLMULTI) < 0)) { - wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode", - __func__); - } - - if (drv->iff_up && - macsec_qca_get_ifflags(drv->ifname, &flags) == 0 && - (flags & IFF_UP) && - macsec_qca_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down", - __func__); - } - - if (drv->pf_sock != -1) - close(drv->pf_sock); + driver_wired_deinit_common(&drv->common); os_free(drv); } @@ -457,6 +190,16 @@ static int macsec_qca_macsec_deinit(void *priv) } +static int macsec_qca_get_capability(void *priv, enum macsec_cap *cap) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + + *cap = MACSEC_CAP_INTEG_AND_CONF_0_30_50; + + return 0; +} + + static int macsec_qca_enable_protect_frames(void *priv, Boolean enabled) { struct macsec_qca_data *drv = priv; @@ -486,19 +229,32 @@ static int macsec_qca_set_replay_protect(void *priv, Boolean enabled, } +static fal_cipher_suite_e macsec_qca_cs_type_get(u64 cs) +{ + if (cs == CS_ID_GCM_AES_128) + return FAL_CIPHER_SUITE_AES_GCM_128; + if (cs == CS_ID_GCM_AES_256) + return FAL_CIPHER_SUITE_AES_GCM_256; + return FAL_CIPHER_SUITE_MAX; +} + + static int macsec_qca_set_current_cipher_suite(void *priv, u64 cs) { - if (cs != CS_ID_GCM_AES_128) { + struct macsec_qca_data *drv = priv; + fal_cipher_suite_e cs_type; + + if (cs != CS_ID_GCM_AES_128 && cs != CS_ID_GCM_AES_256) { wpa_printf(MSG_ERROR, "%s: NOT supported CipherSuite: %016" PRIx64, __func__, cs); return -1; } - /* Support default Cipher Suite 0080020001000001 (GCM-AES-128) */ - wpa_printf(MSG_DEBUG, "%s: default support aes-gcm-128", __func__); + wpa_printf(MSG_DEBUG, "%s: CipherSuite: %016" PRIx64, __func__, cs); - return 0; + cs_type = macsec_qca_cs_type_get(cs); + return nss_macsec_secy_cipher_suite_set(drv->secy_id, cs_type); } @@ -515,16 +271,82 @@ static int macsec_qca_enable_controlled_port(void *priv, Boolean enabled) } -static int macsec_qca_get_receive_lowest_pn(void *priv, u32 channel, u8 an, - u32 *lowest_pn) +static int macsec_qca_lookup_channel(struct channel_map *map, + struct ieee802_1x_mka_sci *sci, + u32 *channel) +{ + u32 i; + + for (i = 0; i < MAXSC; i++) { + if (os_memcmp(&map[i].sci, sci, + sizeof(struct ieee802_1x_mka_sci)) == 0) { + *channel = i; + return 0; + } + } + + return -1; +} + + +static void macsec_qca_register_channel(struct channel_map *map, + struct ieee802_1x_mka_sci *sci, + u32 channel) +{ + os_memcpy(&map[channel].sci, sci, sizeof(struct ieee802_1x_mka_sci)); +} + + +static int macsec_qca_lookup_receive_channel(struct macsec_qca_data *drv, + struct receive_sc *sc, + u32 *channel) +{ + return macsec_qca_lookup_channel(drv->receive_channel_map, &sc->sci, + channel); +} + + +static void macsec_qca_register_receive_channel(struct macsec_qca_data *drv, + struct receive_sc *sc, + u32 channel) +{ + macsec_qca_register_channel(drv->receive_channel_map, &sc->sci, + channel); +} + + +static int macsec_qca_lookup_transmit_channel(struct macsec_qca_data *drv, + struct transmit_sc *sc, + u32 *channel) +{ + return macsec_qca_lookup_channel(drv->transmit_channel_map, &sc->sci, + channel); +} + + +static void macsec_qca_register_transmit_channel(struct macsec_qca_data *drv, + struct transmit_sc *sc, + u32 channel) +{ + macsec_qca_register_channel(drv->transmit_channel_map, &sc->sci, + channel); +} + + +static int macsec_qca_get_receive_lowest_pn(void *priv, struct receive_sa *sa) { struct macsec_qca_data *drv = priv; int ret = 0; u32 next_pn = 0; bool enabled = FALSE; u32 win; + u32 channel; + + ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel); + if (ret != 0) + return ret; - ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, an, + ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, sa->an, &next_pn); ret += nss_macsec_secy_rx_sc_replay_protect_get(drv->secy_id, channel, &enabled); @@ -532,40 +354,49 @@ static int macsec_qca_get_receive_lowest_pn(void *priv, u32 channel, u8 an, channel, &win); if (enabled) - *lowest_pn = (next_pn > win) ? (next_pn - win) : 1; + sa->lowest_pn = (next_pn > win) ? (next_pn - win) : 1; else - *lowest_pn = next_pn; + sa->lowest_pn = next_pn; - wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, *lowest_pn); + wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, sa->lowest_pn); return ret; } -static int macsec_qca_get_transmit_next_pn(void *priv, u32 channel, u8 an, - u32 *next_pn) +static int macsec_qca_get_transmit_next_pn(void *priv, struct transmit_sa *sa) { struct macsec_qca_data *drv = priv; int ret = 0; + u32 channel; - ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, an, - next_pn); + ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel); + if (ret != 0) + return ret; - wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, *next_pn); + ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, sa->an, + &sa->next_pn); + + wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, sa->next_pn); return ret; } -int macsec_qca_set_transmit_next_pn(void *priv, u32 channel, u8 an, u32 next_pn) +static int macsec_qca_set_transmit_next_pn(void *priv, struct transmit_sa *sa) { struct macsec_qca_data *drv = priv; int ret = 0; + u32 channel; + + ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel); + if (ret != 0) + return ret; - ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, an, - next_pn); + ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an, + sa->next_pn); - wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, next_pn); + wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, sa->next_pn); return ret; } @@ -598,8 +429,7 @@ static int macsec_qca_get_available_receive_sc(void *priv, u32 *channel) } -static int macsec_qca_create_receive_sc(void *priv, u32 channel, - const u8 *sci_addr, u16 sci_port, +static int macsec_qca_create_receive_sc(void *priv, struct receive_sc *sc, unsigned int conf_offset, int validation) { @@ -608,6 +438,13 @@ static int macsec_qca_create_receive_sc(void *priv, u32 channel, fal_rx_prc_lut_t entry; fal_rx_sc_validate_frame_e vf; enum validate_frames validate_frames = validation; + u32 channel; + const u8 *sci_addr = sc->sci.addr; + u16 sci_port = be_to_host16(sc->sci.port); + + ret = macsec_qca_get_available_receive_sc(priv, &channel); + if (ret != 0) + return ret; wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel); @@ -615,8 +452,8 @@ static int macsec_qca_create_receive_sc(void *priv, u32 channel, os_memset(&entry, 0, sizeof(entry)); os_memcpy(entry.sci, sci_addr, ETH_ALEN); - entry.sci[6] = (sci_port >> 8) & 0xf; - entry.sci[7] = sci_port & 0xf; + entry.sci[6] = (sci_port >> 8) & 0xff; + entry.sci[7] = sci_port & 0xff; entry.sci_mask = 0xf; entry.valid = 1; @@ -642,15 +479,22 @@ static int macsec_qca_create_receive_sc(void *priv, u32 channel, channel, drv->replay_window); + macsec_qca_register_receive_channel(drv, sc, channel); + return ret; } -static int macsec_qca_delete_receive_sc(void *priv, u32 channel) +static int macsec_qca_delete_receive_sc(void *priv, struct receive_sc *sc) { struct macsec_qca_data *drv = priv; - int ret = 0; + int ret; fal_rx_prc_lut_t entry; + u32 channel; + + ret = macsec_qca_lookup_receive_channel(priv, sc, &channel); + if (ret != 0) + return ret; wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel); @@ -664,49 +508,91 @@ static int macsec_qca_delete_receive_sc(void *priv, u32 channel) } -static int macsec_qca_create_receive_sa(void *priv, u32 channel, u8 an, - u32 lowest_pn, const u8 *sak) +static int macsec_qca_create_receive_sa(void *priv, struct receive_sa *sa) { struct macsec_qca_data *drv = priv; - int ret = 0; + int ret; fal_rx_sak_t rx_sak; int i = 0; + u32 channel; + fal_rx_prc_lut_t entry; + u32 offset; + + ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel); + if (ret != 0) + return ret; wpa_printf(MSG_DEBUG, "%s, channel=%d, an=%d, lpn=0x%x", - __func__, channel, an, lowest_pn); + __func__, channel, sa->an, sa->lowest_pn); os_memset(&rx_sak, 0, sizeof(rx_sak)); - for (i = 0; i < 16; i++) - rx_sak.sak[i] = sak[15 - i]; + rx_sak.sak_len = sa->pkey->key_len; + if (sa->pkey->key_len == SAK_128_LEN) { + for (i = 0; i < 16; i++) + rx_sak.sak[i] = sa->pkey->key[15 - i]; + } else if (sa->pkey->key_len == SAK_256_LEN) { + for (i = 0; i < 16; i++) { + rx_sak.sak1[i] = sa->pkey->key[15 - i]; + rx_sak.sak[i] = sa->pkey->key[31 - i]; + } + } else { + return -1; + } - ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, an); - ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, an, &rx_sak); + if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_0) + offset = 0; + else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_30) + offset = 30; + else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_50) + offset = 50; + else + return -1; + ret += nss_macsec_secy_rx_prc_lut_get(drv->secy_id, channel, &entry); + entry.offset = offset; + ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry); + ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, sa->an); + ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, sa->an, + &rx_sak); return ret; } -static int macsec_qca_enable_receive_sa(void *priv, u32 channel, u8 an) +static int macsec_qca_enable_receive_sa(void *priv, struct receive_sa *sa) { struct macsec_qca_data *drv = priv; - int ret = 0; + int ret; + u32 channel; + + ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel); + if (ret != 0) + return ret; - wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an); + wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, + sa->an); - ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, an, TRUE); + ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an, + TRUE); return ret; } -static int macsec_qca_disable_receive_sa(void *priv, u32 channel, u8 an) +static int macsec_qca_disable_receive_sa(void *priv, struct receive_sa *sa) { struct macsec_qca_data *drv = priv; - int ret = 0; + int ret; + u32 channel; + + ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel); + if (ret != 0) + return ret; - wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an); + wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, + sa->an); - ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, an, FALSE); + ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an, + FALSE); return ret; } @@ -715,14 +601,12 @@ static int macsec_qca_disable_receive_sa(void *priv, u32 channel, u8 an) static int macsec_qca_get_available_transmit_sc(void *priv, u32 *channel) { struct macsec_qca_data *drv = priv; - int ret = 0; u32 sc_ch = 0; bool in_use = FALSE; for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) { - ret = nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch, - &in_use); - if (ret) + if (nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch, + &in_use)) continue; if (!in_use) { @@ -739,14 +623,19 @@ static int macsec_qca_get_available_transmit_sc(void *priv, u32 *channel) } -static int macsec_qca_create_transmit_sc(void *priv, u32 channel, - const u8 *sci_addr, u16 sci_port, +static int macsec_qca_create_transmit_sc(void *priv, struct transmit_sc *sc, unsigned int conf_offset) { struct macsec_qca_data *drv = priv; - int ret = 0; + int ret; fal_tx_class_lut_t entry; u8 psci[ETH_ALEN + 2]; + u32 channel; + u16 sci_port = be_to_host16(sc->sci.port); + + ret = macsec_qca_get_available_transmit_sc(priv, &channel); + if (ret != 0) + return ret; wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel); @@ -757,9 +646,9 @@ static int macsec_qca_create_transmit_sc(void *priv, u32 channel, entry.action = FAL_TX_CLASS_ACTION_FORWARD; entry.channel = channel; - os_memcpy(psci, sci_addr, ETH_ALEN); - psci[6] = (sci_port >> 8) & 0xf; - psci[7] = sci_port & 0xf; + os_memcpy(psci, sc->sci.addr, ETH_ALEN); + psci[6] = (sci_port >> 8) & 0xff; + psci[7] = sci_port & 0xff; ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry); ret += nss_macsec_secy_tx_sc_create(drv->secy_id, channel, psci, 8); @@ -769,15 +658,22 @@ static int macsec_qca_create_transmit_sc(void *priv, u32 channel, channel, conf_offset); + macsec_qca_register_transmit_channel(drv, sc, channel); + return ret; } -static int macsec_qca_delete_transmit_sc(void *priv, u32 channel) +static int macsec_qca_delete_transmit_sc(void *priv, struct transmit_sc *sc) { struct macsec_qca_data *drv = priv; - int ret = 0; + int ret; fal_tx_class_lut_t entry; + u32 channel; + + ret = macsec_qca_lookup_transmit_channel(priv, sc, &channel); + if (ret != 0) + return ret; wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel); @@ -791,19 +687,23 @@ static int macsec_qca_delete_transmit_sc(void *priv, u32 channel) } -static int macsec_qca_create_transmit_sa(void *priv, u32 channel, u8 an, - u32 next_pn, Boolean confidentiality, - const u8 *sak) +static int macsec_qca_create_transmit_sa(void *priv, struct transmit_sa *sa) { struct macsec_qca_data *drv = priv; - int ret = 0; + int ret; u8 tci = 0; fal_tx_sak_t tx_sak; int i; + u32 channel; + u32 offset; + + ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel); + if (ret != 0) + return ret; wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d, next_pn=0x%x, confidentiality=%d", - __func__, channel, an, next_pn, confidentiality); + __func__, channel, sa->an, sa->next_pn, sa->confidentiality); if (drv->always_include_sci) tci |= TCI_SC; @@ -812,45 +712,81 @@ static int macsec_qca_create_transmit_sa(void *priv, u32 channel, u8 an, else if (drv->use_scb) tci |= TCI_SCB; - if (confidentiality) + if (sa->confidentiality) tci |= TCI_E | TCI_C; os_memset(&tx_sak, 0, sizeof(tx_sak)); - for (i = 0; i < 16; i++) - tx_sak.sak[i] = sak[15 - i]; + tx_sak.sak_len = sa->pkey->key_len; + if (sa->pkey->key_len == SAK_128_LEN) { + for (i = 0; i < 16; i++) + tx_sak.sak[i] = sa->pkey->key[15 - i]; + } else if (sa->pkey->key_len == SAK_256_LEN) { + for (i = 0; i < 16; i++) { + tx_sak.sak1[i] = sa->pkey->key[15 - i]; + tx_sak.sak[i] = sa->pkey->key[31 - i]; + } + } else { + return -1; + } - ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, an, - next_pn); - ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, an, &tx_sak); + if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_0) + offset = 0; + else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_30) + offset = 30; + else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_50) + offset = 50; + else + return -1; + ret += nss_macsec_secy_tx_sc_confidentiality_offset_set(drv->secy_id, + channel, + offset); + ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an, + sa->next_pn); + ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, sa->an, + &tx_sak); ret += nss_macsec_secy_tx_sc_tci_7_2_set(drv->secy_id, channel, (tci >> 2)); - ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, an); + ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, sa->an); return ret; } -static int macsec_qca_enable_transmit_sa(void *priv, u32 channel, u8 an) +static int macsec_qca_enable_transmit_sa(void *priv, struct transmit_sa *sa) { struct macsec_qca_data *drv = priv; - int ret = 0; + int ret; + u32 channel; - wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an); + ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel); + if (ret != 0) + return ret; - ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, an, TRUE); + wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, + sa->an); + + ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an, + TRUE); return ret; } -static int macsec_qca_disable_transmit_sa(void *priv, u32 channel, u8 an) +static int macsec_qca_disable_transmit_sa(void *priv, struct transmit_sa *sa) { struct macsec_qca_data *drv = priv; - int ret = 0; + int ret; + u32 channel; + + ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel); + if (ret != 0) + return ret; - wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an); + wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, + sa->an); - ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, an, FALSE); + ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an, + FALSE); return ret; } @@ -859,14 +795,15 @@ static int macsec_qca_disable_transmit_sa(void *priv, u32 channel, u8 an) const struct wpa_driver_ops wpa_driver_macsec_qca_ops = { .name = "macsec_qca", .desc = "QCA MACsec Ethernet driver", - .get_ssid = macsec_qca_get_ssid, - .get_bssid = macsec_qca_get_bssid, - .get_capa = macsec_qca_get_capa, + .get_ssid = driver_wired_get_ssid, + .get_bssid = driver_wired_get_bssid, + .get_capa = driver_wired_get_capa, .init = macsec_qca_init, .deinit = macsec_qca_deinit, .macsec_init = macsec_qca_macsec_init, .macsec_deinit = macsec_qca_macsec_deinit, + .macsec_get_capability = macsec_qca_get_capability, .enable_protect_frames = macsec_qca_enable_protect_frames, .set_replay_protect = macsec_qca_set_replay_protect, .set_current_cipher_suite = macsec_qca_set_current_cipher_suite, @@ -874,13 +811,11 @@ const struct wpa_driver_ops wpa_driver_macsec_qca_ops = { .get_receive_lowest_pn = macsec_qca_get_receive_lowest_pn, .get_transmit_next_pn = macsec_qca_get_transmit_next_pn, .set_transmit_next_pn = macsec_qca_set_transmit_next_pn, - .get_available_receive_sc = macsec_qca_get_available_receive_sc, .create_receive_sc = macsec_qca_create_receive_sc, .delete_receive_sc = macsec_qca_delete_receive_sc, .create_receive_sa = macsec_qca_create_receive_sa, .enable_receive_sa = macsec_qca_enable_receive_sa, .disable_receive_sa = macsec_qca_disable_receive_sa, - .get_available_transmit_sc = macsec_qca_get_available_transmit_sc, .create_transmit_sc = macsec_qca_create_transmit_sc, .delete_transmit_sc = macsec_qca_delete_transmit_sc, .create_transmit_sa = macsec_qca_create_transmit_sa, diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c index 9440f0127235..614c4521e6ff 100644 --- a/src/drivers/driver_ndis.c +++ b/src/drivers/driver_ndis.c @@ -1220,12 +1220,16 @@ static int wpa_driver_ndis_set_pmkid(struct wpa_driver_ndis_data *drv) } -static int wpa_driver_ndis_add_pmkid(void *priv, const u8 *bssid, - const u8 *pmkid) +static int wpa_driver_ndis_add_pmkid(void *priv, + struct wpa_pmkid_params *params) { struct wpa_driver_ndis_data *drv = priv; struct ndis_pmkid_entry *entry, *prev; + const u8 *bssid = params->bssid; + const u8 *pmkid = params->pmkid; + if (!bssid || !pmkid) + return -1; if (drv->no_of_pmkid == 0) return 0; @@ -1261,12 +1265,16 @@ static int wpa_driver_ndis_add_pmkid(void *priv, const u8 *bssid, } -static int wpa_driver_ndis_remove_pmkid(void *priv, const u8 *bssid, - const u8 *pmkid) +static int wpa_driver_ndis_remove_pmkid(void *priv, + struct wpa_pmkid_params *params) { struct wpa_driver_ndis_data *drv = priv; struct ndis_pmkid_entry *entry, *prev; + const u8 *bssid = params->bssid; + const u8 *pmkid = params->pmkid; + if (!bssid || !pmkid) + return -1; if (drv->no_of_pmkid == 0) return 0; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 1210d4356087..871a5d0b4a20 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -29,6 +29,7 @@ #include "common/qca-vendor-attr.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "common/wpa_common.h" #include "l2_packet/l2_packet.h" #include "netlink.h" #include "linux_defines.h" @@ -39,6 +40,29 @@ #include "driver_nl80211.h" +/* support for extack if compilation headers are too old */ +#ifndef NETLINK_EXT_ACK +#define NETLINK_EXT_ACK 11 +enum nlmsgerr_attrs { + NLMSGERR_ATTR_UNUSED, + NLMSGERR_ATTR_MSG, + NLMSGERR_ATTR_OFFS, + NLMSGERR_ATTR_COOKIE, + + __NLMSGERR_ATTR_MAX, + NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1 +}; +#endif +#ifndef NLM_F_CAPPED +#define NLM_F_CAPPED 0x100 +#endif +#ifndef NLM_F_ACK_TLVS +#define NLM_F_ACK_TLVS 0x200 +#endif +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + #ifndef CONFIG_LIBNL20 /* * libnl 1.1 has a bug, it tries to allocate socket numbers densely @@ -129,7 +153,7 @@ static void nl_destroy_handles(struct nl_handle **handle) static void nl80211_register_eloop_read(struct nl_handle **handle, eloop_sock_handler handler, - void *eloop_data) + void *eloop_data, int persist) { #ifdef CONFIG_LIBNL20 /* @@ -150,13 +174,17 @@ static void nl80211_register_eloop_read(struct nl_handle **handle, nl_socket_set_nonblocking(*handle); eloop_register_read_sock(nl_socket_get_fd(*handle), handler, eloop_data, *handle); - *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID); + if (!persist) + *handle = (void *) (((intptr_t) *handle) ^ + ELOOP_SOCKET_INVALID); } -static void nl80211_destroy_eloop_handle(struct nl_handle **handle) +static void nl80211_destroy_eloop_handle(struct nl_handle **handle, int persist) { - *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID); + if (!persist) + *handle = (void *) (((intptr_t) *handle) ^ + ELOOP_SOCKET_INVALID); eloop_unregister_read_sock(nl_socket_get_fd(*handle)); nl_destroy_handles(handle); } @@ -204,6 +232,8 @@ static int nl80211_set_param(void *priv, const char *param); static int nl80211_put_mesh_config(struct nl_msg *msg, struct wpa_driver_mesh_bss_params *params); #endif /* CONFIG_MESH */ +static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, + int reason); /* Converts nl80211_chan_width to a common format */ @@ -295,8 +325,35 @@ static int finish_handler(struct nl_msg *msg, void *arg) static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { + struct nlmsghdr *nlh = (struct nlmsghdr *) err - 1; + int len = nlh->nlmsg_len; + struct nlattr *attrs; + struct nlattr *tb[NLMSGERR_ATTR_MAX + 1]; int *ret = arg; + int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh); + *ret = err->error; + + if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS)) + return NL_SKIP; + + if (!(nlh->nlmsg_flags & NLM_F_CAPPED)) + ack_len += err->msg.nlmsg_len - sizeof(*nlh); + + if (len <= ack_len) + return NL_STOP; + + attrs = (void *) ((unsigned char *) nlh + ack_len); + len -= ack_len; + + nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL); + if (tb[NLMSGERR_ATTR_MSG]) { + len = strnlen((char *) nla_data(tb[NLMSGERR_ATTR_MSG]), + nla_len(tb[NLMSGERR_ATTR_MSG])); + wpa_printf(MSG_ERROR, "nl80211: kernel reports: %*s", + len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG])); + } + return NL_SKIP; } @@ -335,7 +392,7 @@ static int send_and_recv(struct nl80211_global *global, void *valid_data) { struct nl_cb *cb; - int err = -ENOMEM; + int err = -ENOMEM, opt; if (!msg) return -ENOMEM; @@ -344,6 +401,11 @@ static int send_and_recv(struct nl80211_global *global, if (!cb) goto out; + /* try to set NETLINK_EXT_ACK to 1, ignoring errors */ + opt = 1; + setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK, + NETLINK_EXT_ACK, &opt, sizeof(opt)); + err = nl_send_auto_complete(nl_handle, msg); if (err < 0) goto out; @@ -673,6 +735,7 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss) struct nl80211_wiphy_data *w; int wiphy_idx, found = 0; struct i802_bss *tmp_bss; + u8 channel; if (bss->wiphy_data != NULL) return bss->wiphy_data; @@ -692,29 +755,35 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss) dl_list_init(&w->bsss); dl_list_init(&w->drvs); - w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); - if (!w->nl_cb) { - os_free(w); - return NULL; - } - nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); - nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, process_beacon_event, - w); + /* Beacon frames not supported in IEEE 802.11ad */ + if (ieee80211_freq_to_chan(bss->freq, &channel) != + HOSTAPD_MODE_IEEE80211AD) { + w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!w->nl_cb) { + os_free(w); + return NULL; + } + nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, + no_seq_check, NULL); + nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, + process_beacon_event, w); - w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb, - "wiphy beacons"); - if (w->nl_beacons == NULL) { - os_free(w); - return NULL; - } + w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb, + "wiphy beacons"); + if (w->nl_beacons == NULL) { + os_free(w); + return NULL; + } - if (nl80211_register_beacons(bss->drv, w)) { - nl_destroy_handles(&w->nl_beacons); - os_free(w); - return NULL; - } + if (nl80211_register_beacons(bss->drv, w)) { + nl_destroy_handles(&w->nl_beacons); + os_free(w); + return NULL; + } - nl80211_register_eloop_read(&w->nl_beacons, nl80211_recv_beacons, w); + nl80211_register_eloop_read(&w->nl_beacons, + nl80211_recv_beacons, w, 0); + } dl_list_add(&nl80211_wiphys, &w->list); @@ -761,7 +830,8 @@ static void nl80211_put_wiphy_data_ap(struct i802_bss *bss) if (!dl_list_empty(&w->bsss)) return; - nl80211_destroy_eloop_handle(&w->nl_beacons); + if (w->nl_beacons) + nl80211_destroy_eloop_handle(&w->nl_beacons, 0); nl_cb_put(w->nl_cb); dl_list_del(&w->list); @@ -900,7 +970,8 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, nl80211_check_global(drv->global); wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " "interface"); - wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL); + if (wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL) < 0) + return -1; return 1; } @@ -909,19 +980,58 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, static struct wpa_driver_nl80211_data * -nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len) +nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len, + int *init_failed) { struct wpa_driver_nl80211_data *drv; + int res; + + if (init_failed) + *init_failed = 0; dl_list_for_each(drv, &global->interfaces, struct wpa_driver_nl80211_data, list) { - if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) || - have_ifidx(drv, idx, IFIDX_ANY)) + res = wpa_driver_nl80211_own_ifindex(drv, idx, buf, len); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Found matching own interface, but failed to complete reinitialization"); + if (init_failed) + *init_failed = 1; + return drv; + } + if (res > 0 || have_ifidx(drv, idx, IFIDX_ANY)) return drv; } return NULL; } +static void nl80211_refresh_mac(struct wpa_driver_nl80211_data *drv, + int ifindex, int notify) +{ + struct i802_bss *bss; + u8 addr[ETH_ALEN]; + + bss = get_bss_ifindex(drv, ifindex); + if (bss && + linux_get_ifhwaddr(drv->global->ioctl_sock, + bss->ifname, addr) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: %s: failed to re-read MAC address", + bss->ifname); + } else if (bss && os_memcmp(addr, bss->addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Own MAC address on ifindex %d (%s) changed from " + MACSTR " to " MACSTR, + ifindex, bss->ifname, + MAC2STR(bss->addr), MAC2STR(addr)); + os_memcpy(bss->addr, addr, ETH_ALEN); + if (notify) + wpa_supplicant_event(drv->ctx, + EVENT_INTERFACE_MAC_CHANGED, NULL); + } +} + + static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, u8 *buf, size_t len) @@ -934,6 +1044,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, char namebuf[IFNAMSIZ]; char ifname[IFNAMSIZ + 1]; char extra[100], *pos, *end; + int init_failed; extra[0] = '\0'; pos = extra; @@ -978,9 +1089,11 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); - drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); + drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, &init_failed); if (!drv) goto event_newlink; + if (init_failed) + return; /* do not update interface state */ if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { namebuf[0] = '\0'; @@ -989,6 +1102,8 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down " "event since interface %s is up", namebuf); drv->ignore_if_down_event = 0; + /* Re-read MAC address as it may have changed */ + nl80211_refresh_mac(drv, ifi->ifi_index, 1); return; } wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)", @@ -1012,18 +1127,27 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, * dynamic interfaces */ drv = nl80211_find_drv(global, ifi->ifi_index, - buf, len); + buf, len, NULL); if (!drv) return; } } if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) { + namebuf[0] = '\0'; if (if_indextoname(ifi->ifi_index, namebuf) && linux_iface_up(drv->global->ioctl_sock, namebuf) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " "event since interface %s is down", namebuf); + return; + } + wpa_printf(MSG_DEBUG, "nl80211: Interface up (%s/%s)", + namebuf, ifname); + if (os_strcmp(drv->first_bss->ifname, ifname) != 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Not the main interface (%s) - do not indicate interface up", + drv->first_bss->ifname); } else if (if_nametoindex(drv->first_bss->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " "event since interface %s does not exist", @@ -1033,29 +1157,9 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, "event since interface %s is marked " "removed", drv->first_bss->ifname); } else { - struct i802_bss *bss; - u8 addr[ETH_ALEN]; - /* Re-read MAC address as it may have changed */ - bss = get_bss_ifindex(drv, ifi->ifi_index); - if (bss && - linux_get_ifhwaddr(drv->global->ioctl_sock, - bss->ifname, addr) < 0) { - wpa_printf(MSG_DEBUG, - "nl80211: %s: failed to re-read MAC address", - bss->ifname); - } else if (bss && - os_memcmp(addr, bss->addr, ETH_ALEN) != 0) { - wpa_printf(MSG_DEBUG, - "nl80211: Own MAC address on ifindex %d (%s) changed from " - MACSTR " to " MACSTR, - ifi->ifi_index, bss->ifname, - MAC2STR(bss->addr), - MAC2STR(addr)); - os_memcpy(bss->addr, addr, ETH_ALEN); - } + nl80211_refresh_mac(drv, ifi->ifi_index, 0); - wpa_printf(MSG_DEBUG, "nl80211: Interface up"); drv->if_disabled = 0; wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL); @@ -1157,7 +1261,7 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); - drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); + drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, NULL); if (ifi->ifi_family == AF_BRIDGE && brid && drv) { /* device has been removed from bridge */ @@ -1181,16 +1285,108 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, } +struct nl80211_get_assoc_freq_arg { + struct wpa_driver_nl80211_data *drv; + unsigned int assoc_freq; + unsigned int ibss_freq; + u8 assoc_bssid[ETH_ALEN]; + u8 assoc_ssid[SSID_MAX_LEN]; + u8 assoc_ssid_len; +}; + +static int nl80211_get_assoc_freq_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *bss[NL80211_BSS_MAX + 1]; + static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = { + [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC }, + [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC }, + [NL80211_BSS_STATUS] = { .type = NLA_U32 }, + }; + struct nl80211_get_assoc_freq_arg *ctx = arg; + enum nl80211_bss_status status; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[NL80211_ATTR_BSS] || + nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], + bss_policy) || + !bss[NL80211_BSS_STATUS]) + return NL_SKIP; + + status = nla_get_u32(bss[NL80211_BSS_STATUS]); + if (status == NL80211_BSS_STATUS_ASSOCIATED && + bss[NL80211_BSS_FREQUENCY]) { + ctx->assoc_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz", + ctx->assoc_freq); + } + if (status == NL80211_BSS_STATUS_IBSS_JOINED && + bss[NL80211_BSS_FREQUENCY]) { + ctx->ibss_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz", + ctx->ibss_freq); + } + if (status == NL80211_BSS_STATUS_ASSOCIATED && + bss[NL80211_BSS_BSSID]) { + os_memcpy(ctx->assoc_bssid, + nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); + wpa_printf(MSG_DEBUG, "nl80211: Associated with " + MACSTR, MAC2STR(ctx->assoc_bssid)); + } + + if (status == NL80211_BSS_STATUS_ASSOCIATED && + bss[NL80211_BSS_INFORMATION_ELEMENTS]) { + const u8 *ie, *ssid; + size_t ie_len; + + ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]); + ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); + ssid = get_ie(ie, ie_len, WLAN_EID_SSID); + if (ssid && ssid[1] > 0 && ssid[1] <= SSID_MAX_LEN) { + ctx->assoc_ssid_len = ssid[1]; + os_memcpy(ctx->assoc_ssid, ssid + 2, ssid[1]); + } + } + + return NL_SKIP; +} + + +int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid) +{ + struct nl_msg *msg; + int ret; + struct nl80211_get_assoc_freq_arg arg; + + msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN); + os_memset(&arg, 0, sizeof(arg)); + arg.drv = drv; + ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler, + &arg); + if (ret == 0) { + os_memcpy(ssid, arg.assoc_ssid, arg.assoc_ssid_len); + return arg.assoc_ssid_len; + } + wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d (%s)", + ret, strerror(-ret)); + return ret; +} + + unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv) { struct nl_msg *msg; int ret; - struct nl80211_bss_info_arg arg; + struct nl80211_get_assoc_freq_arg arg; msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN); os_memset(&arg, 0, sizeof(arg)); arg.drv = drv; - ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); + ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler, + &arg); if (ret == 0) { unsigned int freq = drv->nlmode == NL80211_IFTYPE_ADHOC ? arg.ibss_freq : arg.assoc_freq; @@ -1273,7 +1469,7 @@ int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, { struct nl_msg *msg; - sig->current_signal = -9999; + sig->current_signal = -WPA_INVALID_NOISE; sig->current_txrate = 0; if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) || @@ -1335,7 +1531,7 @@ int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv, { struct nl_msg *msg; - sig_change->current_noise = 9999; + sig_change->current_noise = WPA_INVALID_NOISE; sig_change->frequency = drv->assoc_freq; msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); @@ -1505,7 +1701,7 @@ static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global) nl80211_register_eloop_read(&global->nl_event, wpa_driver_nl80211_event_receive, - global->nl_cb); + global->nl_cb, 0); return 0; @@ -1871,7 +2067,7 @@ static void nl80211_mgmt_handle_register_eloop(struct i802_bss *bss) { nl80211_register_eloop_read(&bss->nl_mgmt, wpa_driver_nl80211_event_receive, - bss->nl_cb); + bss->nl_cb, 0); } @@ -1884,6 +2080,25 @@ static int nl80211_register_action_frame(struct i802_bss *bss, } +static int nl80211_init_connect_handle(struct i802_bss *bss) +{ + if (bss->nl_connect) { + wpa_printf(MSG_DEBUG, + "nl80211: Connect handle already created (nl_connect=%p)", + bss->nl_connect); + return -1; + } + + bss->nl_connect = nl_create_handle(bss->nl_cb, "connect"); + if (!bss->nl_connect) + return -1; + nl80211_register_eloop_read(&bss->nl_connect, + wpa_driver_nl80211_event_receive, + bss->nl_cb, 1); + return 0; +} + + static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; @@ -1894,7 +2109,9 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP " "handle %p", bss->nl_mgmt); - if (drv->nlmode == NL80211_IFTYPE_ADHOC) { + if (drv->nlmode == NL80211_IFTYPE_ADHOC || + ((drv->capa.flags & WPA_DRIVER_FLAGS_SAE) && + !(drv->capa.flags & WPA_DRIVER_FLAGS_SME))) { u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4); /* register for any AUTH message */ @@ -1906,7 +2123,7 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) if (nl80211_register_action_frame(bss, (u8 *) "\x01\x04", 2) < 0) ret = -1; #endif /* CONFIG_INTERWORKING */ -#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) +#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP) /* GAS Initial Request */ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0) ret = -1; @@ -1931,7 +2148,7 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) /* Protected GAS Comeback Response */ if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0d", 2) < 0) ret = -1; -#endif /* CONFIG_P2P || CONFIG_INTERWORKING */ +#endif /* CONFIG_P2P || CONFIG_INTERWORKING || CONFIG_DPP */ #ifdef CONFIG_P2P /* P2P Public Action */ if (nl80211_register_action_frame(bss, @@ -1944,6 +2161,13 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) 5) < 0) ret = -1; #endif /* CONFIG_P2P */ +#ifdef CONFIG_DPP + /* DPP Public Action */ + if (nl80211_register_action_frame(bss, + (u8 *) "\x04\x09\x50\x6f\x9a\x1a", + 6) < 0) + ret = -1; +#endif /* CONFIG_DPP */ #ifdef CONFIG_IEEE80211W /* SA Query Response */ if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0) @@ -1976,6 +2200,11 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) /* WNM-Sleep Mode Response */ if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0) ret = -1; +#ifdef CONFIG_WNM + /* WNM - Collocated Interference Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x0b", 2) < 0) + ret = -1; +#endif /* CONFIG_WNM */ #ifdef CONFIG_HS20 /* WNM-Notification */ @@ -2070,6 +2299,9 @@ static int nl80211_action_subscribe_ap(struct i802_bss *bss) /* RRM Measurement Report */ if (nl80211_register_action_frame(bss, (u8 *) "\x05\x01", 2) < 0) ret = -1; + /* RRM Link Measurement Report */ + if (nl80211_register_action_frame(bss, (u8 *) "\x05\x03", 2) < 0) + ret = -1; /* RRM Neighbor Report Request */ if (nl80211_register_action_frame(bss, (u8 *) "\x05\x04", 2) < 0) ret = -1; @@ -2141,9 +2373,6 @@ static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss) if (nl80211_register_spurious_class3(bss)) goto out_err; - if (nl80211_get_wiphy_data_ap(bss) == NULL) - goto out_err; - nl80211_mgmt_handle_register_eloop(bss); return 0; @@ -2178,7 +2407,7 @@ static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason) return; wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p " "(%s)", bss->nl_mgmt, reason); - nl80211_destroy_eloop_handle(&bss->nl_mgmt); + nl80211_destroy_eloop_handle(&bss->nl_mgmt, 0); nl80211_put_wiphy_data_ap(bss); } @@ -2394,16 +2623,20 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, if (drv->vendor_cmd_test_avail) qca_vendor_test(drv); + nl80211_init_connect_handle(bss); + return 0; } -static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv) +static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss) { struct nl_msg *msg; + struct wpa_driver_nl80211_data *drv = bss->drv; wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)", drv->ifindex); + nl80211_put_wiphy_data_ap(bss); msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON); return send_and_recv_msgs(drv, msg, NULL, NULL); } @@ -2438,9 +2671,11 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) wpa_printf(MSG_INFO, "nl80211: Failed to remove " "interface %s from bridge %s: %s", bss->ifname, bss->brname, strerror(errno)); - if (drv->rtnl_sk) - nl80211_handle_destroy(drv->rtnl_sk); } + + if (drv->rtnl_sk) + nl80211_handle_destroy(drv->rtnl_sk); + if (bss->added_bridge) { if (linux_set_iface_flags(drv->global->ioctl_sock, bss->brname, 0) < 0) @@ -2456,7 +2691,7 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) nl80211_remove_monitor_interface(drv); if (is_ap_interface(drv->nlmode)) - wpa_driver_nl80211_del_beacon(drv); + wpa_driver_nl80211_del_beacon(bss); if (drv->eapol_sock >= 0) { eloop_unregister_read_sock(drv->eapol_sock); @@ -2505,6 +2740,9 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) nl80211_del_p2pdev(bss); } + if (bss->nl_connect) + nl80211_destroy_eloop_handle(&bss->nl_connect, 1); + nl80211_destroy_bss(drv->first_bss); os_free(drv->filter_ssids); @@ -2530,30 +2768,30 @@ static u32 wpa_alg_to_cipher_suite(enum wpa_alg alg, size_t key_len) switch (alg) { case WPA_ALG_WEP: if (key_len == 5) - return WLAN_CIPHER_SUITE_WEP40; - return WLAN_CIPHER_SUITE_WEP104; + return RSN_CIPHER_SUITE_WEP40; + return RSN_CIPHER_SUITE_WEP104; case WPA_ALG_TKIP: - return WLAN_CIPHER_SUITE_TKIP; + return RSN_CIPHER_SUITE_TKIP; case WPA_ALG_CCMP: - return WLAN_CIPHER_SUITE_CCMP; + return RSN_CIPHER_SUITE_CCMP; case WPA_ALG_GCMP: - return WLAN_CIPHER_SUITE_GCMP; + return RSN_CIPHER_SUITE_GCMP; case WPA_ALG_CCMP_256: - return WLAN_CIPHER_SUITE_CCMP_256; + return RSN_CIPHER_SUITE_CCMP_256; case WPA_ALG_GCMP_256: - return WLAN_CIPHER_SUITE_GCMP_256; + return RSN_CIPHER_SUITE_GCMP_256; case WPA_ALG_IGTK: - return WLAN_CIPHER_SUITE_AES_CMAC; + return RSN_CIPHER_SUITE_AES_128_CMAC; case WPA_ALG_BIP_GMAC_128: - return WLAN_CIPHER_SUITE_BIP_GMAC_128; + return RSN_CIPHER_SUITE_BIP_GMAC_128; case WPA_ALG_BIP_GMAC_256: - return WLAN_CIPHER_SUITE_BIP_GMAC_256; + return RSN_CIPHER_SUITE_BIP_GMAC_256; case WPA_ALG_BIP_CMAC_256: - return WLAN_CIPHER_SUITE_BIP_CMAC_256; + return RSN_CIPHER_SUITE_BIP_CMAC_256; case WPA_ALG_SMS4: - return WLAN_CIPHER_SUITE_SMS4; + return RSN_CIPHER_SUITE_SMS4; case WPA_ALG_KRK: - return WLAN_CIPHER_SUITE_KRK; + return RSN_CIPHER_SUITE_KRK; case WPA_ALG_NONE: case WPA_ALG_PMK: wpa_printf(MSG_ERROR, "nl80211: Unexpected encryption algorithm %d", @@ -2571,21 +2809,21 @@ static u32 wpa_cipher_to_cipher_suite(unsigned int cipher) { switch (cipher) { case WPA_CIPHER_CCMP_256: - return WLAN_CIPHER_SUITE_CCMP_256; + return RSN_CIPHER_SUITE_CCMP_256; case WPA_CIPHER_GCMP_256: - return WLAN_CIPHER_SUITE_GCMP_256; + return RSN_CIPHER_SUITE_GCMP_256; case WPA_CIPHER_CCMP: - return WLAN_CIPHER_SUITE_CCMP; + return RSN_CIPHER_SUITE_CCMP; case WPA_CIPHER_GCMP: - return WLAN_CIPHER_SUITE_GCMP; + return RSN_CIPHER_SUITE_GCMP; case WPA_CIPHER_TKIP: - return WLAN_CIPHER_SUITE_TKIP; + return RSN_CIPHER_SUITE_TKIP; case WPA_CIPHER_WEP104: - return WLAN_CIPHER_SUITE_WEP104; + return RSN_CIPHER_SUITE_WEP104; case WPA_CIPHER_WEP40: - return WLAN_CIPHER_SUITE_WEP40; + return RSN_CIPHER_SUITE_WEP40; case WPA_CIPHER_GTK_NOT_USED: - return WLAN_CIPHER_SUITE_NO_GROUP_ADDR; + return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED; } return 0; @@ -2598,19 +2836,19 @@ static int wpa_cipher_to_cipher_suites(unsigned int ciphers, u32 suites[], int num_suites = 0; if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP_256) - suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP_256; + suites[num_suites++] = RSN_CIPHER_SUITE_CCMP_256; if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP_256) - suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP_256; + suites[num_suites++] = RSN_CIPHER_SUITE_GCMP_256; if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP) - suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP; + suites[num_suites++] = RSN_CIPHER_SUITE_CCMP; if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP) - suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP; + suites[num_suites++] = RSN_CIPHER_SUITE_GCMP; if (num_suites < max_suites && ciphers & WPA_CIPHER_TKIP) - suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP; + suites[num_suites++] = RSN_CIPHER_SUITE_TKIP; if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP104) - suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104; + suites[num_suites++] = RSN_CIPHER_SUITE_WEP104; if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP40) - suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40; + suites[num_suites++] = RSN_CIPHER_SUITE_WEP40; return num_suites; } @@ -2647,6 +2885,44 @@ static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv, #endif /* CONFIG_DRIVER_NL80211_QCA */ +static int nl80211_set_pmk(struct wpa_driver_nl80211_data *drv, + const u8 *key, size_t key_len, + const u8 *addr) +{ + struct nl_msg *msg = NULL; + int ret; + + /* + * If the authenticator address is not set, assume it is + * the current BSSID. + */ + if (!addr && drv->associated) + addr = drv->bssid; + else if (!addr) + return -1; + + wpa_printf(MSG_DEBUG, "nl80211: Set PMK to the driver for " MACSTR, + MAC2STR(addr)); + wpa_hexdump_key(MSG_DEBUG, "nl80211: PMK", key, key_len); + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_PMK); + if (!msg || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || + nla_put(msg, NL80211_ATTR_PMK, key_len, key)) { + nl80211_nlmsg_clear(msg); + nlmsg_free(msg); + return -ENOBUFS; + } + + ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Set PMK failed: ret=%d (%s)", + ret, strerror(-ret)); + } + + return ret; +} + + static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, @@ -2685,6 +2961,10 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, } #endif /* CONFIG_DRIVER_NL80211_QCA */ + if (alg == WPA_ALG_PMK && + (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) + return nl80211_set_pmk(drv, key, key_len, addr); + if (alg == WPA_ALG_NONE) { msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY); if (!msg) @@ -2868,8 +3148,8 @@ static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params, params->wep_key[i]) || nla_put_u32(msg, NL80211_KEY_CIPHER, params->wep_key_len[i] == 5 ? - WLAN_CIPHER_SUITE_WEP40 : - WLAN_CIPHER_SUITE_WEP104) || + RSN_CIPHER_SUITE_WEP40 : + RSN_CIPHER_SUITE_WEP104) || nla_put_u8(msg, NL80211_KEY_IDX, i) || (i == params->wep_tx_keyidx && nla_put_flag(msg, NL80211_KEY_DEFAULT))) @@ -2885,7 +3165,8 @@ static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params, int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, const u8 *addr, int cmd, u16 reason_code, - int local_state_change) + int local_state_change, + struct nl_handle *nl_connect) { int ret; struct nl_msg *msg; @@ -2899,7 +3180,10 @@ int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, return -1; } - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (nl_connect) + ret = send_and_recv(drv->global, nl_connect, msg, NULL, NULL); + else + ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) { wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: MLME command failed: reason=%u ret=%d (%s)", @@ -2910,20 +3194,22 @@ int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv, - int reason_code) + int reason_code, + struct nl_handle *nl_connect) { int ret; + int drv_associated = drv->associated; wpa_printf(MSG_DEBUG, "%s(reason_code=%d)", __func__, reason_code); nl80211_mark_disconnected(drv); /* Disconnect command doesn't need BSSID - it uses cached value */ ret = wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT, - reason_code, 0); + reason_code, 0, nl_connect); /* * For locally generated disconnect, supplicant already generates a * DEAUTH event, so ignore the event from NL80211. */ - drv->ignore_next_local_disconnect = ret == 0; + drv->ignore_next_local_disconnect = drv_associated && (ret == 0); return ret; } @@ -2934,23 +3220,31 @@ static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss, { struct wpa_driver_nl80211_data *drv = bss->drv; int ret; + int drv_associated = drv->associated; if (drv->nlmode == NL80211_IFTYPE_ADHOC) { nl80211_mark_disconnected(drv); return nl80211_leave_ibss(drv, 1); } - if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) - return wpa_driver_nl80211_disconnect(drv, reason_code); + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { + struct nl_handle *nl_connect = NULL; + + if (bss->use_nl_connect) + nl_connect = bss->nl_connect; + return wpa_driver_nl80211_disconnect(drv, reason_code, + nl_connect); + } wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", __func__, MAC2STR(addr), reason_code); nl80211_mark_disconnected(drv); ret = wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, - reason_code, 0); + reason_code, 0, NULL); /* * For locally generated deauthenticate, supplicant already generates a * DEAUTH event, so ignore the event from NL80211. */ - drv->ignore_next_local_deauth = ret == 0; + drv->ignore_next_local_deauth = drv_associated && (ret == 0); + return ret; } @@ -3020,6 +3314,27 @@ static void nl80211_unmask_11b_rates(struct i802_bss *bss) } +static enum nl80211_auth_type get_nl_auth_type(int wpa_auth_alg) +{ + if (wpa_auth_alg & WPA_AUTH_ALG_OPEN) + return NL80211_AUTHTYPE_OPEN_SYSTEM; + if (wpa_auth_alg & WPA_AUTH_ALG_SHARED) + return NL80211_AUTHTYPE_SHARED_KEY; + if (wpa_auth_alg & WPA_AUTH_ALG_LEAP) + return NL80211_AUTHTYPE_NETWORK_EAP; + if (wpa_auth_alg & WPA_AUTH_ALG_FT) + return NL80211_AUTHTYPE_FT; + if (wpa_auth_alg & WPA_AUTH_ALG_SAE) + return NL80211_AUTHTYPE_SAE; + if (wpa_auth_alg & WPA_AUTH_ALG_FILS) + return NL80211_AUTHTYPE_FILS_SK; + if (wpa_auth_alg & WPA_AUTH_ALG_FILS_SK_PFS) + return NL80211_AUTHTYPE_FILS_SK_PFS; + + return NL80211_AUTHTYPE_MAX; +} + + static int wpa_driver_nl80211_authenticate( struct i802_bss *bss, struct wpa_driver_auth_params *params) { @@ -3095,27 +3410,17 @@ retry: if (params->ie && nla_put(msg, NL80211_ATTR_IE, params->ie_len, params->ie)) goto fail; - if (params->sae_data) { - wpa_hexdump(MSG_DEBUG, " * SAE data", params->sae_data, - params->sae_data_len); - if (nla_put(msg, NL80211_ATTR_SAE_DATA, params->sae_data_len, - params->sae_data)) + if (params->auth_data) { + wpa_hexdump(MSG_DEBUG, " * auth_data", params->auth_data, + params->auth_data_len); + if (nla_put(msg, NL80211_ATTR_SAE_DATA, params->auth_data_len, + params->auth_data)) goto fail; } - if (params->auth_alg & WPA_AUTH_ALG_OPEN) - type = NL80211_AUTHTYPE_OPEN_SYSTEM; - else if (params->auth_alg & WPA_AUTH_ALG_SHARED) - type = NL80211_AUTHTYPE_SHARED_KEY; - else if (params->auth_alg & WPA_AUTH_ALG_LEAP) - type = NL80211_AUTHTYPE_NETWORK_EAP; - else if (params->auth_alg & WPA_AUTH_ALG_FT) - type = NL80211_AUTHTYPE_FT; - else if (params->auth_alg & WPA_AUTH_ALG_SAE) - type = NL80211_AUTHTYPE_SAE; - else - goto fail; + type = get_nl_auth_type(params->auth_alg); wpa_printf(MSG_DEBUG, " * Auth Type %d", type); - if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type)) + if (type == NL80211_AUTHTYPE_MAX || + nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type)) goto fail; if (params->local_state_change) { wpa_printf(MSG_DEBUG, " * Local state change only"); @@ -3127,11 +3432,11 @@ retry: msg = NULL; if (ret) { wpa_dbg(drv->ctx, MSG_DEBUG, - "nl80211: MLME command failed (auth): ret=%d (%s)", - ret, strerror(-ret)); + "nl80211: MLME command failed (auth): count=%d ret=%d (%s)", + count, ret, strerror(-ret)); count++; - if (ret == -EALREADY && count == 1 && params->bssid && - !params->local_state_change) { + if ((ret == -EALREADY || ret == -EEXIST) && count == 1 && + params->bssid && !params->local_state_change) { /* * mac80211 does not currently accept new * authentication if we are already authenticated. As a @@ -3515,6 +3820,147 @@ static int nl80211_set_mesh_config(void *priv, #endif /* CONFIG_MESH */ +static int nl80211_put_beacon_rate(struct nl_msg *msg, const u64 flags, + struct wpa_driver_ap_params *params) +{ + struct nlattr *bands, *band; + struct nl80211_txrate_vht vht_rate; + + if (!params->freq || + (params->beacon_rate == 0 && + params->rate_type == BEACON_RATE_LEGACY)) + return 0; + + bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES); + if (!bands) + return -1; + + switch (params->freq->mode) { + case HOSTAPD_MODE_IEEE80211B: + case HOSTAPD_MODE_IEEE80211G: + band = nla_nest_start(msg, NL80211_BAND_2GHZ); + break; + case HOSTAPD_MODE_IEEE80211A: + band = nla_nest_start(msg, NL80211_BAND_5GHZ); + break; + case HOSTAPD_MODE_IEEE80211AD: + band = nla_nest_start(msg, NL80211_BAND_60GHZ); + break; + default: + return 0; + } + + if (!band) + return -1; + + os_memset(&vht_rate, 0, sizeof(vht_rate)); + + switch (params->rate_type) { + case BEACON_RATE_LEGACY: + if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY)) { + wpa_printf(MSG_INFO, + "nl80211: Driver does not support setting Beacon frame rate (legacy)"); + return -1; + } + + if (nla_put_u8(msg, NL80211_TXRATE_LEGACY, + (u8) params->beacon_rate / 5) || + nla_put(msg, NL80211_TXRATE_HT, 0, NULL) || + (params->freq->vht_enabled && + nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate), + &vht_rate))) + return -1; + + wpa_printf(MSG_DEBUG, " * beacon_rate = legacy:%u (* 100 kbps)", + params->beacon_rate); + break; + case BEACON_RATE_HT: + if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_HT)) { + wpa_printf(MSG_INFO, + "nl80211: Driver does not support setting Beacon frame rate (HT)"); + return -1; + } + if (nla_put(msg, NL80211_TXRATE_LEGACY, 0, NULL) || + nla_put_u8(msg, NL80211_TXRATE_HT, params->beacon_rate) || + (params->freq->vht_enabled && + nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate), + &vht_rate))) + return -1; + wpa_printf(MSG_DEBUG, " * beacon_rate = HT-MCS %u", + params->beacon_rate); + break; + case BEACON_RATE_VHT: + if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_VHT)) { + wpa_printf(MSG_INFO, + "nl80211: Driver does not support setting Beacon frame rate (VHT)"); + return -1; + } + vht_rate.mcs[0] = BIT(params->beacon_rate); + if (nla_put(msg, NL80211_TXRATE_LEGACY, 0, NULL)) + return -1; + if (nla_put(msg, NL80211_TXRATE_HT, 0, NULL)) + return -1; + if (nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate), + &vht_rate)) + return -1; + wpa_printf(MSG_DEBUG, " * beacon_rate = VHT-MCS %u", + params->beacon_rate); + break; + } + + nla_nest_end(msg, band); + nla_nest_end(msg, bands); + + return 0; +} + + +static int nl80211_set_multicast_to_unicast(struct i802_bss *bss, + int multicast_to_unicast) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_MULTICAST_TO_UNICAST); + if (!msg || + (multicast_to_unicast && + nla_put_flag(msg, NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED))) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to build NL80211_CMD_SET_MULTICAST_TO_UNICAST msg for %s", + bss->ifname); + nlmsg_free(msg); + return -ENOBUFS; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + + switch (ret) { + case 0: + wpa_printf(MSG_DEBUG, + "nl80211: multicast to unicast %s on interface %s", + multicast_to_unicast ? "enabled" : "disabled", + bss->ifname); + break; + case -EOPNOTSUPP: + if (!multicast_to_unicast) + break; + wpa_printf(MSG_INFO, + "nl80211: multicast to unicast not supported on interface %s", + bss->ifname); + break; + default: + wpa_printf(MSG_ERROR, + "nl80211: %s multicast to unicast failed with %d (%s) on interface %s", + multicast_to_unicast ? "enabling" : "disabling", + ret, strerror(-ret), bss->ifname); + break; + } + + return ret; +} + + static int wpa_driver_nl80211_set_ap(void *priv, struct wpa_driver_ap_params *params) { @@ -3538,6 +3984,9 @@ static int wpa_driver_nl80211_set_ap(void *priv, beacon_set); if (beacon_set) cmd = NL80211_CMD_SET_BEACON; + else if (!drv->device_ap_sme && !drv->use_monitor && + !nl80211_get_wiphy_data_ap(bss)) + return -ENOBUFS; wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head", params->head, params->head_len); @@ -3545,6 +3994,8 @@ static int wpa_driver_nl80211_set_ap(void *priv, params->tail, params->tail_len); wpa_printf(MSG_DEBUG, "nl80211: ifindex=%d", bss->ifindex); wpa_printf(MSG_DEBUG, "nl80211: beacon_int=%d", params->beacon_int); + wpa_printf(MSG_DEBUG, "nl80211: beacon_rate=%u", params->beacon_rate); + wpa_printf(MSG_DEBUG, "nl80211: rate_type=%d", params->rate_type); wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period); wpa_hexdump_ascii(MSG_DEBUG, "nl80211: ssid", params->ssid, params->ssid_len); @@ -3554,6 +4005,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, nla_put(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, params->tail) || nl80211_put_beacon_int(msg, params->beacon_int) || + nl80211_put_beacon_rate(msg, drv->capa.flags, params) || nl80211_put_dtim_period(msg, params->dtim_period) || nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) goto fail; @@ -3616,9 +4068,9 @@ static int wpa_driver_nl80211_set_ap(void *priv, params->key_mgmt_suites); num_suites = 0; if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X) - suites[num_suites++] = WLAN_AKM_SUITE_8021X; + suites[num_suites++] = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; if (params->key_mgmt_suites & WPA_KEY_MGMT_PSK) - suites[num_suites++] = WLAN_AKM_SUITE_PSK; + suites[num_suites++] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; if (num_suites && nla_put(msg, NL80211_ATTR_AKM_SUITES, num_suites * sizeof(u32), suites)) @@ -3664,7 +4116,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, smps_mode = NL80211_SMPS_OFF; break; } - if (nla_put_u32(msg, NL80211_ATTR_SMPS_MODE, smps_mode)) + if (nla_put_u8(msg, NL80211_ATTR_SMPS_MODE, smps_mode)) goto fail; } @@ -3731,6 +4183,8 @@ static int wpa_driver_nl80211_set_ap(void *priv, nl80211_set_bss(bss, params->cts_protect, params->preamble, params->short_slot_time, params->ht_opmode, params->isolate, params->basic_rates); + nl80211_set_multicast_to_unicast(bss, + params->multicast_to_unicast); if (beacon_set && params->freq && params->freq->bandwidth != bss->bandwidth) { wpa_printf(MSG_DEBUG, @@ -4464,6 +4918,7 @@ static void nl80211_teardown_ap(struct i802_bss *bss) else nl80211_mgmt_unsubscribe(bss, "AP teardown"); + nl80211_put_wiphy_data_ap(bss); bss->beacon_set = 0; } @@ -4808,10 +5263,54 @@ fail: } +static int nl80211_put_fils_connect_params(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params, + struct nl_msg *msg) +{ + if (params->fils_erp_username_len) { + wpa_hexdump_ascii(MSG_DEBUG, " * FILS ERP EMSKname/username", + params->fils_erp_username, + params->fils_erp_username_len); + if (nla_put(msg, NL80211_ATTR_FILS_ERP_USERNAME, + params->fils_erp_username_len, + params->fils_erp_username)) + return -1; + } + + if (params->fils_erp_realm_len) { + wpa_hexdump_ascii(MSG_DEBUG, " * FILS ERP Realm", + params->fils_erp_realm, + params->fils_erp_realm_len); + if (nla_put(msg, NL80211_ATTR_FILS_ERP_REALM, + params->fils_erp_realm_len, params->fils_erp_realm)) + return -1; + } + + wpa_printf(MSG_DEBUG, " * FILS ERP next seq %u", + params->fils_erp_next_seq_num); + if (nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, + params->fils_erp_next_seq_num)) + return -1; + + if (params->fils_erp_rrk_len) { + wpa_printf(MSG_DEBUG, " * FILS ERP rRK (len=%lu)", + (unsigned long) params->fils_erp_rrk_len); + if (nla_put(msg, NL80211_ATTR_FILS_ERP_RRK, + params->fils_erp_rrk_len, params->fils_erp_rrk)) + return -1; + } + + return 0; +} + + static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params, struct nl_msg *msg) { + if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER)) + return -1; + if (params->bssid) { wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, MAC2STR(params->bssid)); @@ -4912,40 +5411,64 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B || - params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { - int mgmt = WLAN_AKM_SUITE_PSK; + params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 || + params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA256 || + params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA384 || + params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA256 || + params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA384 || + params->key_mgmt_suite == WPA_KEY_MGMT_OWE || + params->key_mgmt_suite == WPA_KEY_MGMT_DPP) { + int mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; switch (params->key_mgmt_suite) { case WPA_KEY_MGMT_CCKM: - mgmt = WLAN_AKM_SUITE_CCKM; + mgmt = RSN_AUTH_KEY_MGMT_CCKM; break; case WPA_KEY_MGMT_IEEE8021X: - mgmt = WLAN_AKM_SUITE_8021X; + mgmt = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; break; case WPA_KEY_MGMT_FT_IEEE8021X: - mgmt = WLAN_AKM_SUITE_FT_8021X; + mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X; break; case WPA_KEY_MGMT_FT_PSK: - mgmt = WLAN_AKM_SUITE_FT_PSK; + mgmt = RSN_AUTH_KEY_MGMT_FT_PSK; break; case WPA_KEY_MGMT_IEEE8021X_SHA256: - mgmt = WLAN_AKM_SUITE_8021X_SHA256; + mgmt = RSN_AUTH_KEY_MGMT_802_1X_SHA256; break; case WPA_KEY_MGMT_PSK_SHA256: - mgmt = WLAN_AKM_SUITE_PSK_SHA256; + mgmt = RSN_AUTH_KEY_MGMT_PSK_SHA256; break; case WPA_KEY_MGMT_OSEN: - mgmt = WLAN_AKM_SUITE_OSEN; + mgmt = RSN_AUTH_KEY_MGMT_OSEN; break; case WPA_KEY_MGMT_IEEE8021X_SUITE_B: - mgmt = WLAN_AKM_SUITE_8021X_SUITE_B; + mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; break; case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: - mgmt = WLAN_AKM_SUITE_8021X_SUITE_B_192; + mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; + break; + case WPA_KEY_MGMT_FILS_SHA256: + mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA256; + break; + case WPA_KEY_MGMT_FILS_SHA384: + mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA384; + break; + case WPA_KEY_MGMT_FT_FILS_SHA256: + mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256; + break; + case WPA_KEY_MGMT_FT_FILS_SHA384: + mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384; + break; + case WPA_KEY_MGMT_OWE: + mgmt = RSN_AUTH_KEY_MGMT_OWE; + break; + case WPA_KEY_MGMT_DPP: + mgmt = RSN_AUTH_KEY_MGMT_DPP; break; case WPA_KEY_MGMT_PSK: default: - mgmt = WLAN_AKM_SUITE_PSK; + mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; break; } wpa_printf(MSG_DEBUG, " * akm=0x%x", mgmt); @@ -4953,6 +5476,14 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, return -1; } + /* Add PSK in case of 4-way handshake offload */ + if (params->psk && + (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) { + wpa_hexdump_key(MSG_DEBUG, " * PSK", params->psk, 32); + if (nla_put(msg, NL80211_ATTR_PMK, 32, params->psk)) + return -1; + } + if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT)) return -1; @@ -4964,10 +5495,6 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))) return -1; - if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED && - nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED)) - return -1; - if (params->rrm_used) { u32 drv_rrm_flags = drv->capa.rrm_flags; if ((!((drv_rrm_flags & @@ -5000,13 +5527,23 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, drv->connect_reassoc = 1; } + if ((params->auth_alg & WPA_AUTH_ALG_FILS) && + nl80211_put_fils_connect_params(drv, params, msg) != 0) + return -1; + + if ((params->auth_alg & WPA_AUTH_ALG_SAE) && + (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) && + nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT)) + return -1; + return 0; } static int wpa_driver_nl80211_try_connect( struct wpa_driver_nl80211_data *drv, - struct wpa_driver_associate_params *params) + struct wpa_driver_associate_params *params, + struct nl_handle *nl_connect) { struct nl_msg *msg; enum nl80211_auth_type type; @@ -5034,6 +5571,15 @@ static int wpa_driver_nl80211_try_connect( if (ret) goto fail; + if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED && + nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED)) + goto fail; + + if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_OPTIONAL && + (drv->capa.flags & WPA_DRIVER_FLAGS_MFP_OPTIONAL) && + nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_OPTIONAL)) + goto fail; + algs = 0; if (params->auth_alg & WPA_AUTH_ALG_OPEN) algs++; @@ -5041,25 +5587,20 @@ static int wpa_driver_nl80211_try_connect( algs++; if (params->auth_alg & WPA_AUTH_ALG_LEAP) algs++; + if (params->auth_alg & WPA_AUTH_ALG_FILS) + algs++; + if (params->auth_alg & WPA_AUTH_ALG_FT) + algs++; if (algs > 1) { wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic " "selection"); goto skip_auth_type; } - if (params->auth_alg & WPA_AUTH_ALG_OPEN) - type = NL80211_AUTHTYPE_OPEN_SYSTEM; - else if (params->auth_alg & WPA_AUTH_ALG_SHARED) - type = NL80211_AUTHTYPE_SHARED_KEY; - else if (params->auth_alg & WPA_AUTH_ALG_LEAP) - type = NL80211_AUTHTYPE_NETWORK_EAP; - else if (params->auth_alg & WPA_AUTH_ALG_FT) - type = NL80211_AUTHTYPE_FT; - else - goto fail; - + type = get_nl_auth_type(params->auth_alg); wpa_printf(MSG_DEBUG, " * Auth Type %d", type); - if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type)) + if (type == NL80211_AUTHTYPE_MAX || + nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type)) goto fail; skip_auth_type: @@ -5067,7 +5608,11 @@ skip_auth_type: if (ret) goto fail; - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (nl_connect) + ret = send_and_recv(drv->global, nl_connect, msg, NULL, NULL); + else + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d " @@ -5086,7 +5631,8 @@ fail: static int wpa_driver_nl80211_connect( struct wpa_driver_nl80211_data *drv, - struct wpa_driver_associate_params *params) + struct wpa_driver_associate_params *params, + struct nl_handle *nl_connect) { int ret; @@ -5096,7 +5642,7 @@ static int wpa_driver_nl80211_connect( else os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN); - ret = wpa_driver_nl80211_try_connect(drv, params); + ret = wpa_driver_nl80211_try_connect(drv, params, nl_connect); if (ret == -EALREADY) { /* * cfg80211 does not currently accept new connections if @@ -5107,9 +5653,9 @@ static int wpa_driver_nl80211_connect( "disconnecting before reassociation " "attempt"); if (wpa_driver_nl80211_disconnect( - drv, WLAN_REASON_PREV_AUTH_NOT_VALID)) + drv, WLAN_REASON_PREV_AUTH_NOT_VALID, nl_connect)) return -1; - ret = wpa_driver_nl80211_try_connect(drv, params); + ret = wpa_driver_nl80211_try_connect(drv, params, nl_connect); } return ret; } @@ -5134,10 +5680,18 @@ static int wpa_driver_nl80211_associate( if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { enum nl80211_iftype nlmode = params->p2p ? NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; + struct nl_handle *nl_connect = NULL; if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0) return -1; - return wpa_driver_nl80211_connect(drv, params); + if (params->auth_alg & WPA_AUTH_ALG_SAE) { + nl_connect = bss->nl_connect; + bss->use_nl_connect = 1; + } else { + bss->use_nl_connect = 0; + } + + return wpa_driver_nl80211_connect(drv, params, nl_connect); } nl80211_mark_disconnected(drv); @@ -5152,6 +5706,26 @@ static int wpa_driver_nl80211_associate( if (ret) goto fail; + if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED && + nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED)) + goto fail; + + if (params->fils_kek) { + wpa_printf(MSG_DEBUG, " * FILS KEK (len=%u)", + (unsigned int) params->fils_kek_len); + if (nla_put(msg, NL80211_ATTR_FILS_KEK, params->fils_kek_len, + params->fils_kek)) + goto fail; + } + if (params->fils_nonces) { + wpa_hexdump(MSG_DEBUG, " * FILS nonces (for AAD)", + params->fils_nonces, + params->fils_nonces_len); + if (nla_put(msg, NL80211_ATTR_FILS_NONCES, + params->fils_nonces_len, params->fils_nonces)) + goto fail; + } + ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { @@ -5568,6 +6142,17 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 }, [NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 }, [NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 }, + [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, + [NL80211_STA_INFO_ACK_SIGNAL] = { .type = NLA_U8 }, + }; + struct nlattr *rate[NL80211_RATE_INFO_MAX + 1]; + static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { + [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 }, + [NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 }, + [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 }, + [NL80211_RATE_INFO_VHT_MCS] = { .type = NLA_U8 }, + [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG }, + [NL80211_RATE_INFO_VHT_NSS] = { .type = NLA_U8 }, }; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), @@ -5619,6 +6204,72 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) if (stats[NL80211_STA_INFO_TX_FAILED]) data->tx_retry_failed = nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]); + if (stats[NL80211_STA_INFO_SIGNAL]) + data->signal = nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]); + if (stats[NL80211_STA_INFO_ACK_SIGNAL]) { + data->last_ack_rssi = + nla_get_u8(stats[NL80211_STA_INFO_ACK_SIGNAL]); + data->flags |= STA_DRV_DATA_LAST_ACK_RSSI; + } + + if (stats[NL80211_STA_INFO_TX_BITRATE] && + nla_parse_nested(rate, NL80211_RATE_INFO_MAX, + stats[NL80211_STA_INFO_TX_BITRATE], + rate_policy) == 0) { + if (rate[NL80211_RATE_INFO_BITRATE32]) + data->current_tx_rate = + nla_get_u32(rate[NL80211_RATE_INFO_BITRATE32]); + else if (rate[NL80211_RATE_INFO_BITRATE]) + data->current_tx_rate = + nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]); + + if (rate[NL80211_RATE_INFO_MCS]) { + data->tx_mcs = nla_get_u8(rate[NL80211_RATE_INFO_MCS]); + data->flags |= STA_DRV_DATA_TX_MCS; + } + if (rate[NL80211_RATE_INFO_VHT_MCS]) { + data->tx_vhtmcs = + nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]); + data->flags |= STA_DRV_DATA_TX_VHT_MCS; + } + if (rate[NL80211_RATE_INFO_SHORT_GI]) + data->flags |= STA_DRV_DATA_TX_SHORT_GI; + if (rate[NL80211_RATE_INFO_VHT_NSS]) { + data->tx_vht_nss = + nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]); + data->flags |= STA_DRV_DATA_TX_VHT_NSS; + } + } + + if (stats[NL80211_STA_INFO_RX_BITRATE] && + nla_parse_nested(rate, NL80211_RATE_INFO_MAX, + stats[NL80211_STA_INFO_RX_BITRATE], + rate_policy) == 0) { + if (rate[NL80211_RATE_INFO_BITRATE32]) + data->current_rx_rate = + nla_get_u32(rate[NL80211_RATE_INFO_BITRATE32]); + else if (rate[NL80211_RATE_INFO_BITRATE]) + data->current_rx_rate = + nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]); + + if (rate[NL80211_RATE_INFO_MCS]) { + data->rx_mcs = + nla_get_u8(rate[NL80211_RATE_INFO_MCS]); + data->flags |= STA_DRV_DATA_RX_MCS; + } + if (rate[NL80211_RATE_INFO_VHT_MCS]) { + data->rx_vhtmcs = + nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]); + data->flags |= STA_DRV_DATA_RX_VHT_MCS; + } + if (rate[NL80211_RATE_INFO_SHORT_GI]) + data->flags |= STA_DRV_DATA_RX_SHORT_GI; + if (rate[NL80211_RATE_INFO_VHT_NSS]) { + data->rx_vht_nss = + nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]); + data->flags |= STA_DRV_DATA_RX_VHT_NSS; + } + } return NL_SKIP; } @@ -5629,8 +6280,6 @@ static int i802_read_sta_data(struct i802_bss *bss, { struct nl_msg *msg; - os_memset(data, 0, sizeof(*data)); - if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_STATION)) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) { nlmsg_free(msg); @@ -5648,6 +6297,7 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs, struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct nlattr *txq, *params; + int res; msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY); if (!msg) @@ -5693,7 +6343,11 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs, nla_nest_end(msg, txq); - if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + res = send_and_recv_msgs(drv, msg, NULL, NULL); + wpa_printf(MSG_DEBUG, + "nl80211: TX queue param set: queue=%d aifs=%d cw_min=%d cw_max=%d burst_time=%d --> res=%d", + queue, aifs, cw_min, cw_max, burst_time, res); + if (res == 0) return 0; msg = NULL; fail: @@ -5736,6 +6390,7 @@ static int i802_get_inact_sec(void *priv, const u8 *addr) struct hostap_sta_driver_data data; int ret; + os_memset(&data, 0, sizeof(data)); data.inactive_msec = (unsigned long) -1; ret = i802_read_sta_data(priv, &data, addr); if (ret == -ENOENT) @@ -5761,6 +6416,14 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_mgmt mgmt; + u8 channel; + + if (ieee80211_freq_to_chan(bss->freq, &channel) == + HOSTAPD_MODE_IEEE80211AD) { + /* Deauthentication is not used in DMG/IEEE 802.11ad; + * disassociate the STA instead. */ + return i802_sta_disassoc(priv, own_addr, addr, reason); + } if (is_mesh_interface(drv->nlmode)) return -1; @@ -5942,8 +6605,16 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; char name[IFNAMSIZ + 1]; + union wpa_event_data event; + int ret; + + ret = os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid); + if (ret >= (int) sizeof(name)) + wpa_printf(MSG_WARNING, + "nl80211: WDS interface name was truncated"); + else if (ret < 0) + return ret; - os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid); if (ifname_wds) os_strlcpy(ifname_wds, name, IFNAMSIZ + 1); @@ -5960,6 +6631,14 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, linux_br_add_if(drv->global->ioctl_sock, bridge_ifname, name) < 0) return -1; + + os_memset(&event, 0, sizeof(event)); + event.wds_sta_interface.sta_addr = addr; + event.wds_sta_interface.ifname = name; + event.wds_sta_interface.istatus = INTERFACE_ADDED; + wpa_supplicant_event(bss->ctx, + EVENT_WDS_STA_INTERFACE_STATUS, + &event); } if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) { wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA " @@ -5973,6 +6652,12 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, i802_set_sta_vlan(priv, addr, bss->ifname, 0); nl80211_remove_iface(drv, if_nametoindex(name)); + os_memset(&event, 0, sizeof(event)); + event.wds_sta_interface.sta_addr = addr; + event.wds_sta_interface.ifname = name; + event.wds_sta_interface.istatus = INTERFACE_REMOVED; + wpa_supplicant_event(bss->ctx, EVENT_WDS_STA_INTERFACE_STATUS, + &event); return 0; } } @@ -6026,8 +6711,10 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, bss->br_ifindex = br_ifindex; if (linux_br_get(in_br, ifname) == 0) { - if (os_strcmp(in_br, brname) == 0) + if (os_strcmp(in_br, brname) == 0) { + bss->already_in_bridge = 1; return 0; /* already in the bridge */ + } wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from " "bridge %s", ifname, in_br); @@ -6036,7 +6723,7 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_ERROR, "nl80211: Failed to " "remove interface %s from bridge " "%s: %s", - ifname, brname, strerror(errno)); + ifname, in_br, strerror(errno)); return -1; } } @@ -6124,7 +6811,7 @@ static void *i802_init(struct hostapd_data *hapd, add_ifidx(drv, br_ifindex, drv->ifindex); #ifdef CONFIG_LIBNL3_ROUTE - if (bss->added_if_into_bridge) { + if (bss->added_if_into_bridge || bss->already_in_bridge) { drv->rtnl_sk = nl_socket_alloc(); if (drv->rtnl_sk == NULL) { wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock"); @@ -6474,7 +7161,7 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss, wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context"); nl80211_teardown_ap(bss); if (!bss->added_if && !drv->first_bss->next) - wpa_driver_nl80211_del_beacon(drv); + wpa_driver_nl80211_del_beacon(bss); nl80211_destroy_bss(bss); if (!bss->added_if) i802_set_iface_flags(bss, 0); @@ -6598,6 +7285,14 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss, os_memcpy(hdr->addr2, src, ETH_ALEN); os_memcpy(hdr->addr3, bssid, ETH_ALEN); + if (os_memcmp(bss->addr, src, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "nl80211: Use random TA " MACSTR, + MAC2STR(src)); + os_memcpy(bss->rand_addr, src, ETH_ALEN); + } else { + os_memset(bss->rand_addr, 0, ETH_ALEN); + } + if (is_ap_interface(drv->nlmode) && (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) || (int) freq == bss->freq || drv->device_ap_sme || @@ -6745,7 +7440,7 @@ static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report) } else if (bss->nl_preq) { wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request " "reporting nl_preq=%p", bss->nl_preq); - nl80211_destroy_eloop_handle(&bss->nl_preq); + nl80211_destroy_eloop_handle(&bss->nl_preq, 0); } return 0; } @@ -6770,7 +7465,7 @@ static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report) nl80211_register_eloop_read(&bss->nl_preq, wpa_driver_nl80211_event_receive, - bss->nl_cb); + bss->nl_cb, 0); return 0; @@ -6836,7 +7531,7 @@ static int wpa_driver_nl80211_deinit_ap(void *priv) struct wpa_driver_nl80211_data *drv = bss->drv; if (!is_ap_interface(drv->nlmode)) return -1; - wpa_driver_nl80211_del_beacon(drv); + wpa_driver_nl80211_del_beacon(bss); bss->beacon_set = 0; /* @@ -6856,7 +7551,7 @@ static int wpa_driver_nl80211_stop_ap(void *priv) struct wpa_driver_nl80211_data *drv = bss->drv; if (!is_ap_interface(drv->nlmode)) return -1; - wpa_driver_nl80211_del_beacon(drv); + wpa_driver_nl80211_del_beacon(bss); bss->beacon_set = 0; return 0; } @@ -7085,7 +7780,7 @@ static void nl80211_global_deinit(void *priv) nl_destroy_handles(&global->nl); if (global->nl_event) - nl80211_destroy_eloop_handle(&global->nl_event); + nl80211_destroy_eloop_handle(&global->nl_event, 0); nl_cb_put(global->nl_cb); @@ -7104,14 +7799,24 @@ static const char * nl80211_get_radio_name(void *priv) } -static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid, - const u8 *pmkid) +static int nl80211_pmkid(struct i802_bss *bss, int cmd, + struct wpa_pmkid_params *params) { struct nl_msg *msg; + const size_t PMK_MAX_LEN = 48; /* current cfg80211 limit */ if (!(msg = nl80211_bss_msg(bss, 0, cmd)) || - (pmkid && nla_put(msg, NL80211_ATTR_PMKID, 16, pmkid)) || - (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))) { + (params->pmkid && + nla_put(msg, NL80211_ATTR_PMKID, 16, params->pmkid)) || + (params->bssid && + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) || + (params->ssid_len && + nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) || + (params->fils_cache_id && + nla_put(msg, NL80211_ATTR_FILS_CACHE_ID, 2, + params->fils_cache_id)) || + (params->pmk_len && params->pmk_len <= PMK_MAX_LEN && + nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) { nlmsg_free(msg); return -ENOBUFS; } @@ -7120,28 +7825,68 @@ static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid, } -static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) +static int nl80211_add_pmkid(void *priv, struct wpa_pmkid_params *params) { struct i802_bss *bss = priv; - wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid)); - return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid); + int ret; + + if (params->bssid) + wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, + MAC2STR(params->bssid)); + else if (params->fils_cache_id && params->ssid_len) { + wpa_printf(MSG_DEBUG, + "nl80211: Add PMKSA for cache id %02x%02x SSID %s", + params->fils_cache_id[0], params->fils_cache_id[1], + wpa_ssid_txt(params->ssid, params->ssid_len)); + } + + ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params); + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: NL80211_CMD_SET_PMKSA failed: %d (%s)", + ret, strerror(-ret)); + } + + return ret; } -static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) +static int nl80211_remove_pmkid(void *priv, struct wpa_pmkid_params *params) { struct i802_bss *bss = priv; - wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR, - MAC2STR(bssid)); - return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid); + int ret; + + if (params->bssid) + wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR, + MAC2STR(params->bssid)); + else if (params->fils_cache_id && params->ssid_len) { + wpa_printf(MSG_DEBUG, + "nl80211: Delete PMKSA for cache id %02x%02x SSID %s", + params->fils_cache_id[0], params->fils_cache_id[1], + wpa_ssid_txt(params->ssid, params->ssid_len)); + } + + ret = nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params); + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: NL80211_CMD_DEL_PMKSA failed: %d (%s)", + ret, strerror(-ret)); + } + + return ret; } static int nl80211_flush_pmkid(void *priv) { struct i802_bss *bss = priv; + struct nl_msg *msg; + wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs"); - return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_FLUSH_PMKSA); + if (!msg) + return -ENOBUFS; + return send_and_recv_msgs(bss->drv, msg, NULL, NULL); } @@ -7336,7 +8081,7 @@ static void nl80211_set_rekey_info(void *priv, const u8 *kek, size_t kek_len, if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) || !(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) || nla_put(msg, NL80211_REKEY_DATA_KEK, kek_len, kek) || - nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck) || + (kck_len && nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck)) || nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN, replay_ctr)) { nl80211_nlmsg_clear(msg); @@ -7424,6 +8169,7 @@ static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, static int nl80211_set_power_save(struct i802_bss *bss, int enabled) { struct nl_msg *msg; + int ret; if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_POWER_SAVE)) || nla_put_u32(msg, NL80211_ATTR_PS_STATE, @@ -7431,7 +8177,15 @@ static int nl80211_set_power_save(struct i802_bss *bss, int enabled) nlmsg_free(msg); return -ENOBUFS; } - return send_and_recv_msgs(bss->drv, msg, NULL, NULL); + + ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL); + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Setting PS state %s failed: %d (%s)", + enabled ? "enabled" : "disabled", + ret, strerror(-ret)); + } + return ret; } @@ -7545,6 +8299,7 @@ static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer) struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; enum nl80211_tdls_operation nl80211_oper; + int res; if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) return -EOPNOTSUPP; @@ -7580,7 +8335,11 @@ static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer) return -ENOBUFS; } - return send_and_recv_msgs(drv, msg, NULL, NULL); + res = send_and_recv_msgs(drv, msg, NULL, NULL); + wpa_printf(MSG_DEBUG, "nl80211: TDLS_OPER: oper=%d mac=" MACSTR + " --> res=%d (%s)", nl80211_oper, MAC2STR(peer), res, + strerror(-res)); + return res; } @@ -7738,6 +8497,8 @@ static int driver_nl80211_read_sta_data(void *priv, const u8 *addr) { struct i802_bss *bss = priv; + + os_memset(data, 0, sizeof(*data)); return i802_read_sta_data(bss, data, addr); } @@ -7842,7 +8603,7 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) "brname=%s\n" "addr=" MACSTR "\n" "freq=%d\n" - "%s%s%s%s%s", + "%s%s%s%s%s%s", bss->ifindex, bss->ifname, bss->brname, @@ -7851,6 +8612,7 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) bss->beacon_set ? "beacon_set=1\n" : "", bss->added_if_into_bridge ? "added_if_into_bridge=1\n" : "", + bss->already_in_bridge ? "already_in_bridge=1\n" : "", bss->added_bridge ? "added_bridge=1\n" : "", bss->in_deinit ? "in_deinit=1\n" : "", bss->if_dynamic ? "if_dynamic=1\n" : ""); @@ -8026,8 +8788,9 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings) return -EOPNOTSUPP; } - if ((drv->nlmode != NL80211_IFTYPE_AP) && - (drv->nlmode != NL80211_IFTYPE_P2P_GO)) + if (drv->nlmode != NL80211_IFTYPE_AP && + drv->nlmode != NL80211_IFTYPE_P2P_GO && + drv->nlmode != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; /* @@ -8388,6 +9151,95 @@ static int nl80211_roaming(void *priv, int allowed, const u8 *bssid) return send_and_recv_msgs(drv, msg, NULL, NULL); } + + +static int nl80211_disable_fils(void *priv, int disable) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *params; + + wpa_printf(MSG_DEBUG, "nl80211: Disable FILS=%d", disable); + + if (!drv->set_wifi_conf_vendor_cmd_avail) + return -1; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION) || + !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS, + disable)) { + nlmsg_free(msg); + return -1; + } + nla_nest_end(msg, params); + + return send_and_recv_msgs(drv, msg, NULL, NULL); +} + + +/* Reserved QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID value for wpa_supplicant */ +#define WPA_SUPPLICANT_CLIENT_ID 1 + +static int nl80211_set_bssid_blacklist(void *priv, unsigned int num_bssid, + const u8 *bssid) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *params, *nlbssids, *attr; + unsigned int i; + + wpa_printf(MSG_DEBUG, "nl80211: Set blacklist BSSID (num=%u)", + num_bssid); + + if (!drv->roam_vendor_cmd_avail) + return -1; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_ROAM) || + !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD, + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID) || + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID, + WPA_SUPPLICANT_CLIENT_ID) || + nla_put_u32(msg, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID, + num_bssid)) + goto fail; + + nlbssids = nla_nest_start( + msg, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS); + if (!nlbssids) + goto fail; + + for (i = 0; i < num_bssid; i++) { + attr = nla_nest_start(msg, i); + if (!attr) + goto fail; + if (nla_put(msg, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID, + ETH_ALEN, &bssid[i * ETH_ALEN])) + goto fail; + wpa_printf(MSG_DEBUG, "nl80211: BSSID[%u]: " MACSTR, i, + MAC2STR(&bssid[i * ETH_ALEN])); + nla_nest_end(msg, attr); + } + nla_nest_end(msg, nlbssids); + nla_nest_end(msg, params); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + +fail: + nlmsg_free(msg); + return -1; +} + #endif /* CONFIG_DRIVER_NL80211_QCA */ @@ -8470,11 +9322,14 @@ static int nl80211_put_mesh_config(struct nl_msg *msg, return -1; if (((params->flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) && - nla_put_u32(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, - params->auto_plinks)) || + nla_put_u8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, + params->auto_plinks)) || ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS) && nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS, - params->max_peer_links))) + params->max_peer_links)) || + ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD) && + nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD, + params->rssi_threshold))) return -1; /* @@ -9307,6 +10162,304 @@ static int nl80211_p2p_lo_stop(void *priv) return send_and_recv_msgs(drv, msg, NULL, NULL); } + +static int nl80211_set_tdls_mode(void *priv, int tdls_external_control) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *params; + int ret; + u32 tdls_mode; + + wpa_printf(MSG_DEBUG, + "nl80211: Set TDKS mode: tdls_external_control=%d", + tdls_external_control); + + if (tdls_external_control == 1) + tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT | + QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL; + else + tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS)) + goto fail; + + params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!params) + goto fail; + + if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE, + tdls_mode)) + goto fail; + + nla_nest_end(msg, params); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: Set TDLS mode failed: ret=%d (%s)", + ret, strerror(-ret)); + goto fail; + } + return 0; +fail: + nlmsg_free(msg); + return -1; +} + + +#ifdef CONFIG_MBO + +static enum mbo_transition_reject_reason +nl80211_mbo_reject_reason_mapping(enum qca_wlan_btm_candidate_status status) +{ + switch (status) { + case QCA_STATUS_REJECT_EXCESSIVE_FRAME_LOSS_EXPECTED: + return MBO_TRANSITION_REJECT_REASON_FRAME_LOSS; + case QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED: + return MBO_TRANSITION_REJECT_REASON_DELAY; + case QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY: + return MBO_TRANSITION_REJECT_REASON_QOS_CAPACITY; + case QCA_STATUS_REJECT_LOW_RSSI: + return MBO_TRANSITION_REJECT_REASON_RSSI; + case QCA_STATUS_REJECT_HIGH_INTERFERENCE: + return MBO_TRANSITION_REJECT_REASON_INTERFERENCE; + case QCA_STATUS_REJECT_UNKNOWN: + default: + return MBO_TRANSITION_REJECT_REASON_UNSPECIFIED; + } +} + + +static void nl80211_parse_btm_candidate_info(struct candidate_list *candidate, + struct nlattr *tb[], int num) +{ + enum qca_wlan_btm_candidate_status status; + char buf[50]; + + os_memcpy(candidate->bssid, + nla_data(tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID]), + ETH_ALEN); + + status = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS]); + candidate->is_accept = status == QCA_STATUS_ACCEPT; + candidate->reject_reason = nl80211_mbo_reject_reason_mapping(status); + + if (candidate->is_accept) + os_snprintf(buf, sizeof(buf), "Accepted"); + else + os_snprintf(buf, sizeof(buf), + "Rejected, Reject_reason: %d", + candidate->reject_reason); + wpa_printf(MSG_DEBUG, "nl80211: BSSID[%d]: " MACSTR " %s", + num, MAC2STR(candidate->bssid), buf); +} + + +static int +nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg) +{ + struct wpa_bss_candidate_info *info = arg; + struct candidate_list *candidate = info->candidates; + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1]; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1]; + static struct nla_policy policy[ + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] = { + .minlen = ETH_ALEN + }, + [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS] = { + .type = NLA_U32, + }, + }; + struct nlattr *attr; + int rem; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + u8 num; + + num = info->num; /* number of candidates sent to driver */ + info->num = 0; + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb_msg[NL80211_ATTR_VENDOR_DATA] || + nla_parse_nested(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX, + tb_msg[NL80211_ATTR_VENDOR_DATA], NULL) || + !tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO]) + return NL_SKIP; + + wpa_printf(MSG_DEBUG, + "nl80211: WNM Candidate list received from driver"); + nla_for_each_nested(attr, + tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO], + rem) { + if (info->num >= num || + nla_parse_nested( + tb, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX, + attr, policy) || + !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] || + !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS]) + break; + + nl80211_parse_btm_candidate_info(candidate, tb, info->num); + + candidate++; + info->num++; + } + + return NL_SKIP; +} + + +static struct wpa_bss_candidate_info * +nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *attr, *attr1, *attr2; + struct wpa_bss_candidate_info *info; + u8 i; + int ret; + u8 *pos; + + if (!drv->fetch_bss_trans_status) + return NULL; + + info = os_zalloc(sizeof(*info)); + if (!info) + return NULL; + /* Allocate memory for number of candidates sent to driver */ + info->candidates = os_calloc(params->n_candidates, + sizeof(*info->candidates)); + if (!info->candidates) { + os_free(info); + return NULL; + } + + /* Copy the number of candidates being sent to driver. This is used in + * nl80211_get_bss_transition_status_handler() to limit the number of + * candidates that can be populated in info->candidates and will be + * later overwritten with the actual number of candidates received from + * the driver. + */ + info->num = params->n_candidates; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS)) + goto fail; + + attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!attr) + goto fail; + + if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON, + params->mbo_transition_reason)) + goto fail; + + attr1 = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO); + if (!attr1) + goto fail; + + wpa_printf(MSG_DEBUG, + "nl80211: WNM Candidate list info sending to driver: mbo_transition_reason: %d n_candidates: %d", + params->mbo_transition_reason, params->n_candidates); + pos = params->bssid; + for (i = 0; i < params->n_candidates; i++) { + wpa_printf(MSG_DEBUG, "nl80211: BSSID[%d]: " MACSTR, i, + MAC2STR(pos)); + attr2 = nla_nest_start(msg, i); + if (!attr2 || + nla_put(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID, + ETH_ALEN, pos)) + goto fail; + pos += ETH_ALEN; + nla_nest_end(msg, attr2); + } + + nla_nest_end(msg, attr1); + nla_nest_end(msg, attr); + + ret = send_and_recv_msgs(drv, msg, + nl80211_get_bss_transition_status_handler, + info); + msg = NULL; + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: WNM Get BSS transition status failed: ret=%d (%s)", + ret, strerror(-ret)); + goto fail; + } + return info; + +fail: + nlmsg_free(msg); + os_free(info->candidates); + os_free(info); + return NULL; +} + + +/** + * nl80211_ignore_assoc_disallow - Configure driver to ignore assoc_disallow + * @priv: Pointer to private driver data from wpa_driver_nl80211_init() + * @ignore_assoc_disallow: 0 to not ignore, 1 to ignore + * Returns: 0 on success, -1 on failure + */ +static int nl80211_ignore_assoc_disallow(void *priv, int ignore_disallow) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *attr; + int ret = -1; + + if (!drv->set_wifi_conf_vendor_cmd_avail) + return -1; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION)) + goto fail; + + attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!attr) + goto fail; + + wpa_printf(MSG_DEBUG, "nl80211: Set ignore_assoc_disallow %d", + ignore_disallow); + if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED, + ignore_disallow)) + goto fail; + + nla_nest_end(msg, attr); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: Set ignore_assoc_disallow failed: ret=%d (%s)", + ret, strerror(-ret)); + goto fail; + } + +fail: + nlmsg_free(msg); + return ret; +} + +#endif /* CONFIG_MBO */ + #endif /* CONFIG_DRIVER_NL80211_QCA */ @@ -9434,6 +10587,88 @@ static int nl80211_get_ext_capab(void *priv, enum wpa_driver_if_type type, } +static int nl80211_update_connection_params( + void *priv, struct wpa_driver_associate_params *params, + enum wpa_drv_update_connect_params_mask mask) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret = -1; + enum nl80211_auth_type type; + + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS); + if (!msg) + goto fail; + + wpa_printf(MSG_DEBUG, "nl80211: Update connection params (ifindex=%d)", + drv->ifindex); + + if ((mask & WPA_DRV_UPDATE_ASSOC_IES) && params->wpa_ie) { + if (nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len, + params->wpa_ie)) + goto fail; + wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie, + params->wpa_ie_len); + } + + if (mask & WPA_DRV_UPDATE_AUTH_TYPE) { + type = get_nl_auth_type(params->auth_alg); + if (type == NL80211_AUTHTYPE_MAX || + nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type)) + goto fail; + wpa_printf(MSG_DEBUG, " * Auth Type %d", type); + } + + if ((mask & WPA_DRV_UPDATE_FILS_ERP_INFO) && + nl80211_put_fils_connect_params(drv, params, msg)) + goto fail; + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: Update connect params command failed: ret=%d (%s)", + ret, strerror(-ret)); + +fail: + nlmsg_free(msg); + return ret; +} + + +static int nl80211_send_external_auth_status(void *priv, + struct external_auth *params) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg = NULL; + int ret = -1; + + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: External auth status: %u", params->status); + + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_EXTERNAL_AUTH); + if (!msg || + nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, params->status) || + nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, + params->ssid) || + nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid)) + goto fail; + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, + "nl80211: External Auth status update failed: ret=%d (%s)", + ret, strerror(-ret)); + goto fail; + } +fail: + nlmsg_free(msg); + return ret; +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -9543,6 +10778,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .get_ifindex = nl80211_get_ifindex, #ifdef CONFIG_DRIVER_NL80211_QCA .roaming = nl80211_roaming, + .disable_fils = nl80211_disable_fils, .do_acs = wpa_driver_do_acs, .set_band = nl80211_set_band, .get_pref_freq_list = nl80211_get_pref_freq_list, @@ -9550,7 +10786,15 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .p2p_lo_start = nl80211_p2p_lo_start, .p2p_lo_stop = nl80211_p2p_lo_stop, .set_default_scan_ies = nl80211_set_default_scan_ies, + .set_tdls_mode = nl80211_set_tdls_mode, +#ifdef CONFIG_MBO + .get_bss_transition_status = nl80211_get_bss_transition_status, + .ignore_assoc_disallow = nl80211_ignore_assoc_disallow, +#endif /* CONFIG_MBO */ + .set_bssid_blacklist = nl80211_set_bssid_blacklist, #endif /* CONFIG_DRIVER_NL80211_QCA */ .configure_data_frame_filters = nl80211_configure_data_frame_filters, .get_ext_capab = nl80211_get_ext_capab, + .update_connect_params = nl80211_update_connection_params, + .send_external_auth_status = nl80211_send_external_auth_status, }; diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index d0ec48c9f973..bc562ba7a8cc 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -60,11 +60,13 @@ struct i802_bss { char brname[IFNAMSIZ]; unsigned int beacon_set:1; unsigned int added_if_into_bridge:1; + unsigned int already_in_bridge:1; unsigned int added_bridge:1; unsigned int in_deinit:1; unsigned int wdev_id_set:1; unsigned int added_if:1; unsigned int static_ap:1; + unsigned int use_nl_connect:1; u8 addr[ETH_ALEN]; @@ -73,11 +75,12 @@ struct i802_bss { int if_dynamic; void *ctx; - struct nl_handle *nl_preq, *nl_mgmt; + struct nl_handle *nl_preq, *nl_mgmt, *nl_connect; struct nl_cb *nl_cb; struct nl80211_wiphy_data *wiphy_data; struct dl_list wiphy_list; + u8 rand_addr[ETH_ALEN]; }; struct wpa_driver_nl80211_data { @@ -160,6 +163,9 @@ struct wpa_driver_nl80211_data { unsigned int scan_vendor_cmd_avail:1; unsigned int connect_reassoc:1; unsigned int set_wifi_conf_vendor_cmd_avail:1; + unsigned int he_capab_vendor_cmd_avail:1; + unsigned int fetch_bss_trans_status:1; + unsigned int roam_vendor_cmd_avail:1; u64 vendor_scan_cookie; u64 remain_on_chan_cookie; @@ -208,6 +214,8 @@ struct wpa_driver_nl80211_data { * (NL80211_CMD_VENDOR). 0 if no pending scan request. */ int last_scan_cmd; + + struct he_capabilities he_capab; }; struct nl_msg; @@ -228,6 +236,7 @@ int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, void *arg, int use_existing); void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx); unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv); +int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid); enum chan_width convert2width(int width); void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv); struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv, @@ -244,7 +253,8 @@ int wpa_driver_nl80211_set_mode(struct i802_bss *bss, enum nl80211_iftype nlmode); int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, const u8 *addr, int cmd, u16 reason_code, - int local_state_change); + int local_state_change, + struct nl_handle *nl_connect); int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv); void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv); @@ -254,7 +264,8 @@ int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv, int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv); struct hostapd_hw_modes * -nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags); +nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags, + u8 *dfs_domain); int process_global_event(struct nl_msg *msg, void *arg); int process_bss_event(struct nl_msg *msg, void *arg); @@ -282,15 +293,6 @@ int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon, /* driver_nl80211_scan.c */ -struct nl80211_bss_info_arg { - struct wpa_driver_nl80211_data *drv; - struct wpa_scan_results *res; - unsigned int assoc_freq; - unsigned int ibss_freq; - u8 assoc_bssid[ETH_ALEN]; -}; - -int bss_info_handler(struct nl_msg *msg, void *arg); void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx); int wpa_driver_nl80211_scan(struct i802_bss *bss, struct wpa_driver_scan_params *params); @@ -299,7 +301,7 @@ int wpa_driver_nl80211_sched_scan(void *priv, int wpa_driver_nl80211_stop_sched_scan(void *priv); struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv); void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv); -int wpa_driver_nl80211_abort_scan(void *priv); +int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie); int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, struct wpa_driver_scan_params *params); int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len); diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index 6adc3f6d33dc..7b360d209aba 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -12,8 +12,8 @@ #include <netlink/genl/genl.h> #include "utils/common.h" -#include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "common/wpa_common.h" #include "common/qca-vendor.h" #include "common/qca-vendor-attr.h" #include "driver_nl80211.h" @@ -266,40 +266,40 @@ static void wiphy_info_cipher_suites(struct wiphy_info_data *info, c >> 24, (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff); switch (c) { - case WLAN_CIPHER_SUITE_CCMP_256: + case RSN_CIPHER_SUITE_CCMP_256: info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256; break; - case WLAN_CIPHER_SUITE_GCMP_256: + case RSN_CIPHER_SUITE_GCMP_256: info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256; break; - case WLAN_CIPHER_SUITE_CCMP: + case RSN_CIPHER_SUITE_CCMP: info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP; break; - case WLAN_CIPHER_SUITE_GCMP: + case RSN_CIPHER_SUITE_GCMP: info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP; break; - case WLAN_CIPHER_SUITE_TKIP: + case RSN_CIPHER_SUITE_TKIP: info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP; break; - case WLAN_CIPHER_SUITE_WEP104: + case RSN_CIPHER_SUITE_WEP104: info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104; break; - case WLAN_CIPHER_SUITE_WEP40: + case RSN_CIPHER_SUITE_WEP40: info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40; break; - case WLAN_CIPHER_SUITE_AES_CMAC: + case RSN_CIPHER_SUITE_AES_128_CMAC: info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP; break; - case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case RSN_CIPHER_SUITE_BIP_GMAC_128: info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128; break; - case WLAN_CIPHER_SUITE_BIP_GMAC_256: + case RSN_CIPHER_SUITE_BIP_GMAC_256: info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256; break; - case WLAN_CIPHER_SUITE_BIP_CMAC_256: + case RSN_CIPHER_SUITE_BIP_CMAC_256: info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256; break; - case WLAN_CIPHER_SUITE_NO_GROUP_ADDR: + case RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED: info->capa->enc |= WPA_DRIVER_CAPA_ENC_GTK_NOT_USED; break; } @@ -362,6 +362,72 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_RRM)) capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_RRM; + + if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_FILS_STA)) + capa->flags |= WPA_DRIVER_FLAGS_SUPPORT_FILS; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_BEACON_RATE_LEGACY)) + capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_BEACON_RATE_HT)) + capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_HT; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_BEACON_RATE_VHT)) + capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_VHT; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_SET_SCAN_DWELL)) + capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_SCAN_START_TIME) && + ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_BSS_PARENT_TSF) && + ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_SET_SCAN_DWELL)) + capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT; + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA)) + capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA; + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED)) + capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED; + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI)) + capa->flags |= WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI; + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_FILS_SK_OFFLOAD)) + capa->flags |= WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK) && + ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X)) + capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_MFP_OPTIONAL)) + capa->flags |= WPA_DRIVER_FLAGS_MFP_OPTIONAL; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_DFS_OFFLOAD)) + capa->flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD; + +#ifdef CONFIG_MBO + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME) && + ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP) && + ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE) && + ext_feature_isset( + ext_features, len, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION)) + capa->flags |= WPA_DRIVER_FLAGS_OCE_STA; +#endif /* CONFIG_MBO */ } @@ -510,23 +576,22 @@ static void wiphy_info_extended_capab(struct wpa_driver_nl80211_data *drv, nl80211_iftype_str(capa->iftype)); len = nla_len(tb1[NL80211_ATTR_EXT_CAPA]); - capa->ext_capa = os_malloc(len); + capa->ext_capa = os_memdup(nla_data(tb1[NL80211_ATTR_EXT_CAPA]), + len); if (!capa->ext_capa) goto err; - os_memcpy(capa->ext_capa, nla_data(tb1[NL80211_ATTR_EXT_CAPA]), - len); capa->ext_capa_len = len; wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities", capa->ext_capa, capa->ext_capa_len); len = nla_len(tb1[NL80211_ATTR_EXT_CAPA_MASK]); - capa->ext_capa_mask = os_malloc(len); + capa->ext_capa_mask = + os_memdup(nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]), + len); if (!capa->ext_capa_mask) goto err; - os_memcpy(capa->ext_capa_mask, - nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]), len); wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities mask", capa->ext_capa_mask, capa->ext_capa_len); @@ -708,6 +773,15 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) case QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION: drv->set_wifi_conf_vendor_cmd_avail = 1; break; + case QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES: + drv->he_capab_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS: + drv->fetch_bss_trans_status = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_ROAM: + drv->roam_vendor_cmd_avail = 1; + break; #endif /* CONFIG_DRIVER_NL80211_QCA */ } } @@ -744,6 +818,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) capa->max_csa_counters = nla_get_u8(tb[NL80211_ATTR_MAX_CSA_COUNTERS]); + if (tb[NL80211_ATTR_WIPHY_SELF_MANAGED_REG]) + capa->flags |= WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY; + return NL_SKIP; } @@ -877,6 +954,100 @@ static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv) } +static int qca_nl80211_he_capab_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct he_capabilities *he_capab = arg; + struct nlattr *nl_vend; + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX + 1]; + size_t len; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_VENDOR_DATA]) + return NL_SKIP; + + nl_vend = tb[NL80211_ATTR_VENDOR_DATA]; + nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX, + nla_data(nl_vend), nla_len(nl_vend), NULL); + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED]) { + u8 he_supported; + + he_supported = nla_get_u8( + tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED]); + wpa_printf(MSG_DEBUG, "nl80211: HE capabilities supported: %u", + he_supported); + he_capab->he_supported = he_supported; + if (!he_supported) + return NL_SKIP; + } + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]) { + len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]); + + if (len > sizeof(he_capab->phy_cap)) + len = sizeof(he_capab->phy_cap); + os_memcpy(he_capab->phy_cap, + nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]), + len); + } + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_CAPAB]) + he_capab->mac_cap = + nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_CAPAB]); + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_MCS]) + he_capab->mcs = + nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_MCS]); + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_NUM_SS]) + he_capab->ppet.numss_m1 = + nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_NUM_SS]); + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK]) + he_capab->ppet.ru_count = + nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK]); + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]) { + len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]); + + if (len > sizeof(he_capab->ppet.ppet16_ppet8_ru3_ru0)) + len = sizeof(he_capab->ppet.ppet16_ppet8_ru3_ru0); + os_memcpy(he_capab->ppet.ppet16_ppet8_ru3_ru0, + nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]), + len); + } + + return NL_SKIP; +} + + +static void qca_nl80211_check_he_capab(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + int ret; + + if (!drv->he_capab_vendor_cmd_avail) + return; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES)) { + nlmsg_free(msg); + return; + } + + ret = send_and_recv_msgs(drv, msg, qca_nl80211_he_capab_handler, + &drv->he_capab); + if (!ret && drv->he_capab.he_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_HE_CAPABILITIES; +} + + struct features_info { u8 *flags; size_t flags_len; @@ -973,6 +1144,12 @@ static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv) drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS; if (check_feature(QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD, &info)) drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD; + if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA, &info)) + drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA; + if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_AP, &info)) + drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_AP; + if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON, &info)) + drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA_CFON; os_free(info.flags); } @@ -994,7 +1171,19 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK | WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B | - WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192; + WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192 | + WPA_DRIVER_CAPA_KEY_MGMT_OWE | + WPA_DRIVER_CAPA_KEY_MGMT_DPP; + + if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 | + WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 | + WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 | + WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384; + else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD) + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 | + WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384; + drv->capa.auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | WPA_DRIVER_AUTH_LEAP; @@ -1046,8 +1235,10 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; #ifdef CONFIG_DRIVER_NL80211_QCA - qca_nl80211_check_dfs_capa(drv); + if (!(info.capa->flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) + qca_nl80211_check_dfs_capa(drv); qca_nl80211_get_features(drv); + qca_nl80211_check_he_capab(drv); /* * To enable offchannel simultaneous support in wpa_supplicant, the @@ -1069,6 +1260,7 @@ struct phy_info_arg { struct hostapd_hw_modes *modes; int last_mode, last_chan_idx; int failed; + u8 dfs_domain; }; static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa, @@ -1388,14 +1580,13 @@ wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes, mode11g = &modes[mode11g_idx]; mode->num_channels = mode11g->num_channels; - mode->channels = os_malloc(mode11g->num_channels * + mode->channels = os_memdup(mode11g->channels, + mode11g->num_channels * sizeof(struct hostapd_channel_data)); if (mode->channels == NULL) { (*num_modes)--; return modes; /* Could not add 802.11b mode */ } - os_memcpy(mode->channels, mode11g->channels, - mode11g->num_channels * sizeof(struct hostapd_channel_data)); mode->num_rates = 0; mode->rates = os_malloc(4 * sizeof(int)); @@ -1598,6 +1789,20 @@ static void nl80211_reg_rule_vht(struct nlattr *tb[], } +static void nl80211_set_dfs_domain(enum nl80211_dfs_regions region, + u8 *dfs_domain) +{ + if (region == NL80211_DFS_FCC) + *dfs_domain = HOSTAPD_DFS_REGION_FCC; + else if (region == NL80211_DFS_ETSI) + *dfs_domain = HOSTAPD_DFS_REGION_ETSI; + else if (region == NL80211_DFS_JP) + *dfs_domain = HOSTAPD_DFS_REGION_JP; + else + *dfs_domain = 0; +} + + static const char * dfs_domain_name(enum nl80211_dfs_regions region) { switch (region) { @@ -1644,6 +1849,7 @@ static int nl80211_get_reg(struct nl_msg *msg, void *arg) if (tb_msg[NL80211_ATTR_DFS_REGION]) { enum nl80211_dfs_regions dfs_domain; dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]); + nl80211_set_dfs_domain(dfs_domain, &results->dfs_domain); wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)", (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), dfs_domain_name(dfs_domain)); @@ -1715,12 +1921,20 @@ static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv, return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG); + if (drv->capa.flags & WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY) { + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx)) { + nlmsg_free(msg); + return -1; + } + } + return send_and_recv_msgs(drv, msg, nl80211_get_reg, results); } struct hostapd_hw_modes * -nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags, + u8 *dfs_domain) { u32 feat; struct i802_bss *bss = priv; @@ -1732,10 +1946,12 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) .modes = NULL, .last_mode = -1, .failed = 0, + .dfs_domain = 0, }; *num_modes = 0; *flags = 0; + *dfs_domain = 0; feat = get_nl80211_protocol_features(drv); if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) @@ -1756,8 +1972,12 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) os_free(result.modes[i].rates); } os_free(result.modes); + *num_modes = 0; return NULL; } + + *dfs_domain = result.dfs_domain; + return wpa_driver_nl80211_postprocess_modes(result.modes, num_modes); } diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index 762e3acc2807..205b4cd4b082 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -1,6 +1,6 @@ /* * Driver interaction with Linux nl80211/cfg80211 - Event processing - * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi> * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net> * Copyright (c) 2009-2010, Atheros Communications * @@ -131,6 +131,11 @@ static const char * nl80211_command_to_string(enum nl80211_commands cmd) C2S(NL80211_CMD_SET_QOS_MAP) C2S(NL80211_CMD_ADD_TX_TS) C2S(NL80211_CMD_DEL_TX_TS) + C2S(NL80211_CMD_WIPHY_REG_CHANGE) + C2S(NL80211_CMD_PORT_AUTHORIZED) + C2S(NL80211_CMD_EXTERNAL_AUTH) + C2S(NL80211_CMD_STA_OPMODE_CHANGED) + C2S(NL80211_CMD_CONTROL_PORT_FRAME) default: return "NL80211_CMD_UNKNOWN"; } @@ -206,6 +211,7 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, const struct ieee80211_mgmt *mgmt; union wpa_event_data event; u16 status; + int ssid_len; if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) && drv->force_connect_cmd) { @@ -247,6 +253,8 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN); os_memset(&event, 0, sizeof(event)); + event.assoc_info.resp_frame = frame; + event.assoc_info.resp_frame_len = len; if (len > 24 + sizeof(mgmt->u.assoc_resp)) { event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable; event.assoc_info.resp_ies_len = @@ -255,6 +263,16 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, event.assoc_info.freq = drv->assoc_freq; + /* When this association was initiated outside of wpa_supplicant, + * drv->ssid needs to be set here to satisfy later checking. */ + ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid); + if (ssid_len > 0) { + drv->ssid_len = ssid_len; + wpa_printf(MSG_DEBUG, + "nl80211: Set drv->ssid based on scan res info to '%s'", + wpa_ssid_txt(drv->ssid, drv->ssid_len)); + } + nl80211_parse_wmm_params(wmm, &event.assoc_info.wmm_params); wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); @@ -266,15 +284,20 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, struct nlattr *addr, struct nlattr *req_ie, struct nlattr *resp_ie, struct nlattr *timed_out, + struct nlattr *timeout_reason, struct nlattr *authorized, struct nlattr *key_replay_ctr, struct nlattr *ptk_kck, struct nlattr *ptk_kek, - struct nlattr *subnet_status) + struct nlattr *subnet_status, + struct nlattr *fils_erp_next_seq_num, + struct nlattr *fils_pmk, + struct nlattr *fils_pmkid) { union wpa_event_data event; - const u8 *ssid; + const u8 *ssid = NULL; u16 status_code; + int ssid_len; if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { /* @@ -324,6 +347,27 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, } event.assoc_reject.status_code = status_code; event.assoc_reject.timed_out = timed_out != NULL; + if (timed_out && timeout_reason) { + enum nl80211_timeout_reason reason; + + reason = nla_get_u32(timeout_reason); + switch (reason) { + case NL80211_TIMEOUT_SCAN: + event.assoc_reject.timeout_reason = "scan"; + break; + case NL80211_TIMEOUT_AUTH: + event.assoc_reject.timeout_reason = "auth"; + break; + case NL80211_TIMEOUT_ASSOC: + event.assoc_reject.timeout_reason = "assoc"; + break; + default: + break; + } + } + if (fils_erp_next_seq_num) + event.assoc_reject.fils_erp_next_seq_num = + nla_get_u16(fils_erp_next_seq_num); wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); return; } @@ -345,6 +389,10 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, if (ssid && ssid[1] > 0 && ssid[1] <= 32) { drv->ssid_len = ssid[1]; os_memcpy(drv->ssid, ssid + 2, ssid[1]); + wpa_printf(MSG_DEBUG, + "nl80211: Set drv->ssid based on req_ie to '%s'", + wpa_ssid_txt(drv->ssid, + drv->ssid_len)); } } } @@ -355,6 +403,16 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, event.assoc_info.freq = nl80211_get_assoc_freq(drv); + if ((!ssid || ssid[1] == 0 || ssid[1] > 32) && + (ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid)) > 0) { + /* When this connection was initiated outside of wpa_supplicant, + * drv->ssid needs to be set here to satisfy later checking. */ + drv->ssid_len = ssid_len; + wpa_printf(MSG_DEBUG, + "nl80211: Set drv->ssid based on scan res info to '%s'", + wpa_ssid_txt(drv->ssid, drv->ssid_len)); + } + if (authorized && nla_get_u8(authorized)) { event.assoc_info.authorized = 1; wpa_printf(MSG_DEBUG, "nl80211: connection authorized"); @@ -383,6 +441,18 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, event.assoc_info.subnet_status = nla_get_u8(subnet_status); } + if (fils_erp_next_seq_num) + event.assoc_info.fils_erp_next_seq_num = + nla_get_u16(fils_erp_next_seq_num); + + if (fils_pmk) { + event.assoc_info.fils_pmk = nla_data(fils_pmk); + event.assoc_info.fils_pmk_len = nla_len(fils_pmk); + } + + if (fils_pmkid) + event.assoc_info.fils_pmkid = nla_data(fils_pmkid); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); } @@ -516,6 +586,7 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, data.ch_switch.cf2 = nla_get_u32(cf2); bss->freq = data.ch_switch.freq; + drv->assoc_freq = data.ch_switch.freq; wpa_supplicant_event(bss->ctx, EVENT_CH_SWITCH, &data); } @@ -607,7 +678,7 @@ static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv, cookie_val = nla_get_u64(cookie); wpa_printf(MSG_DEBUG, "nl80211: Action TX status:" - " cookie=0%llx%s (ack=%d)", + " cookie=0x%llx%s (ack=%d)", (long long unsigned int) cookie_val, cookie_val == drv->send_action_cookie ? " (match)" : " (unknown)", ack != NULL); @@ -683,12 +754,12 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, * disconnection event for the old AP may show up after * we have started connection with the new AP. */ - wpa_printf(MSG_DEBUG, - "nl80211: Ignore deauth/disassoc event from old AP " - MACSTR - " when already connecting with " MACSTR, - MAC2STR(bssid), - MAC2STR(drv->auth_attempt_bssid)); + wpa_printf(MSG_DEBUG, + "nl80211: Ignore deauth/disassoc event from old AP " + MACSTR + " when already connecting with " MACSTR, + MAC2STR(bssid), + MAC2STR(drv->auth_attempt_bssid)); return; } @@ -831,6 +902,8 @@ static void mlme_event(struct i802_bss *bss, MAC2STR(data + 4 + ETH_ALEN)); if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) && os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 && + (is_zero_ether_addr(bss->rand_addr) || + os_memcmp(bss->rand_addr, data + 4, ETH_ALEN) != 0) && os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) { wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event " "for foreign address", bss->ifname); @@ -1081,6 +1154,16 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s", msg); } + + if (tb[NL80211_ATTR_SCAN_START_TIME_TSF] && + tb[NL80211_ATTR_SCAN_START_TIME_TSF_BSSID]) { + info->scan_start_tsf = + nla_get_u64(tb[NL80211_ATTR_SCAN_START_TIME_TSF]); + os_memcpy(info->scan_start_tsf_bssid, + nla_data(tb[NL80211_ATTR_SCAN_START_TIME_TSF_BSSID]), + ETH_ALEN); + } + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); } @@ -1093,6 +1176,10 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 }, [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_BEACON_LOSS_EVENT] = { .type = NLA_FLAG }, }; struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; enum nl80211_cqm_rssi_threshold_event event; @@ -1114,12 +1201,39 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, return; os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + ed.low_ack.num_packets = + nla_get_u32(cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]); + wpa_printf(MSG_DEBUG, "nl80211: Packet loss event for " MACSTR + " (num_packets %u)", + MAC2STR(ed.low_ack.addr), ed.low_ack.num_packets); wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed); return; } - if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) + if (cqm[NL80211_ATTR_CQM_BEACON_LOSS_EVENT]) { + wpa_printf(MSG_DEBUG, "nl80211: Beacon loss event"); + wpa_supplicant_event(drv->ctx, EVENT_BEACON_LOSS, NULL); + return; + } + + if (cqm[NL80211_ATTR_CQM_TXE_RATE] && + cqm[NL80211_ATTR_CQM_TXE_PKTS] && + cqm[NL80211_ATTR_CQM_TXE_INTVL] && + cqm[NL80211_ATTR_MAC]) { + wpa_printf(MSG_DEBUG, "nl80211: CQM TXE event for " MACSTR + " (rate: %u pkts: %u interval: %u)", + MAC2STR((u8 *) nla_data(cqm[NL80211_ATTR_MAC])), + nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_RATE]), + nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_PKTS]), + nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_INTVL])); + return; + } + + if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) { + wpa_printf(MSG_DEBUG, + "nl80211: Not a CQM RSSI threshold event"); return; + } event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) { @@ -1130,8 +1244,12 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " "event: RSSI low"); ed.signal_change.above_threshold = 0; - } else + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Unknown CQM RSSI threshold event: %d", + event); return; + } res = nl80211_get_link_signal(drv, &sig); if (res == 0) { @@ -1473,6 +1591,13 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv, case NL80211_RADAR_NOP_FINISHED: wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data); break; + case NL80211_RADAR_PRE_CAC_EXPIRED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_PRE_CAC_EXPIRED, + &data); + break; + case NL80211_RADAR_CAC_STARTED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data); + break; default: wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d " "received", event_type); @@ -1650,12 +1775,15 @@ static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv, tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE], - NULL, + NULL, NULL, tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK], - tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS]); + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID]); } @@ -2055,6 +2183,146 @@ static void nl80211_reg_change_event(struct wpa_driver_nl80211_data *drv, } +static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data event; + enum nl80211_external_auth_action act; + + if (!tb[NL80211_ATTR_AKM_SUITES] || + !tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION] || + !tb[NL80211_ATTR_BSSID] || + !tb[NL80211_ATTR_SSID]) + return; + + os_memset(&event, 0, sizeof(event)); + act = nla_get_u32(tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION]); + switch (act) { + case NL80211_EXTERNAL_AUTH_START: + event.external_auth.action = EXT_AUTH_START; + break; + case NL80211_EXTERNAL_AUTH_ABORT: + event.external_auth.action = EXT_AUTH_ABORT; + break; + default: + return; + } + + event.external_auth.key_mgmt_suite = + nla_get_u32(tb[NL80211_ATTR_AKM_SUITES]); + + event.external_auth.ssid_len = nla_len(tb[NL80211_ATTR_SSID]); + if (event.external_auth.ssid_len > SSID_MAX_LEN) + return; + os_memcpy(event.external_auth.ssid, nla_data(tb[NL80211_ATTR_SSID]), + event.external_auth.ssid_len); + + os_memcpy(event.external_auth.bssid, nla_data(tb[NL80211_ATTR_BSSID]), + ETH_ALEN); + + wpa_printf(MSG_DEBUG, + "nl80211: External auth action: %u, AKM: 0x%x", + event.external_auth.action, + event.external_auth.key_mgmt_suite); + wpa_supplicant_event(drv->ctx, EVENT_EXTERNAL_AUTH, &event); +} + + +static void nl80211_port_authorized(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + const u8 *addr; + + if (!tb[NL80211_ATTR_MAC] || + nla_len(tb[NL80211_ATTR_MAC]) != ETH_ALEN) { + wpa_printf(MSG_DEBUG, + "nl80211: Ignore port authorized event without BSSID"); + return; + } + + addr = nla_data(tb[NL80211_ATTR_MAC]); + if (os_memcmp(addr, drv->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Ignore port authorized event for " MACSTR + " (not the currently connected BSSID " MACSTR ")", + MAC2STR(addr), MAC2STR(drv->bssid)); + return; + } + + wpa_supplicant_event(drv->ctx, EVENT_PORT_AUTHORIZED, NULL); +} + + +static void nl80211_sta_opmode_change_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data ed; + u8 smps_mode, max_bw; + + if (!tb[NL80211_ATTR_MAC] || + (!tb[NL80211_ATTR_CHANNEL_WIDTH] && + !tb[NL80211_ATTR_SMPS_MODE] && + !tb[NL80211_ATTR_NSS])) + return; + + ed.sta_opmode.smps_mode = SMPS_INVALID; + ed.sta_opmode.chan_width = CHAN_WIDTH_UNKNOWN; + ed.sta_opmode.rx_nss = 0xff; + ed.sta_opmode.addr = nla_data(tb[NL80211_ATTR_MAC]); + + if (tb[NL80211_ATTR_SMPS_MODE]) { + smps_mode = nla_get_u8(tb[NL80211_ATTR_SMPS_MODE]); + switch (smps_mode) { + case NL80211_SMPS_OFF: + ed.sta_opmode.smps_mode = SMPS_OFF; + break; + case NL80211_SMPS_STATIC: + ed.sta_opmode.smps_mode = SMPS_STATIC; + break; + case NL80211_SMPS_DYNAMIC: + ed.sta_opmode.smps_mode = SMPS_DYNAMIC; + break; + default: + ed.sta_opmode.smps_mode = SMPS_INVALID; + break; + } + } + + if (tb[NL80211_ATTR_CHANNEL_WIDTH]) { + max_bw = nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]); + switch (max_bw) { + case NL80211_CHAN_WIDTH_20_NOHT: + ed.sta_opmode.chan_width = CHAN_WIDTH_20_NOHT; + break; + case NL80211_CHAN_WIDTH_20: + ed.sta_opmode.chan_width = CHAN_WIDTH_20; + break; + case NL80211_CHAN_WIDTH_40: + ed.sta_opmode.chan_width = CHAN_WIDTH_40; + break; + case NL80211_CHAN_WIDTH_80: + ed.sta_opmode.chan_width = CHAN_WIDTH_80; + break; + case NL80211_CHAN_WIDTH_80P80: + ed.sta_opmode.chan_width = CHAN_WIDTH_80P80; + break; + case NL80211_CHAN_WIDTH_160: + ed.sta_opmode.chan_width = CHAN_WIDTH_160; + break; + default: + ed.sta_opmode.chan_width = CHAN_WIDTH_UNKNOWN; + break; + + } + } + + if (tb[NL80211_ATTR_NSS]) + ed.sta_opmode.rx_nss = nla_get_u8(tb[NL80211_ATTR_NSS]); + + wpa_supplicant_event(drv->ctx, EVENT_STATION_OPMODE_CHANGED, &ed); +} + + static void do_process_drv_event(struct i802_bss *bss, int cmd, struct nlattr **tb) { @@ -2113,9 +2381,10 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, case NL80211_CMD_NEW_SCAN_RESULTS: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: New scan results available"); + if (drv->last_scan_cmd != NL80211_CMD_VENDOR) + drv->scan_state = SCAN_COMPLETED; drv->scan_complete_events = 1; if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) { - drv->scan_state = SCAN_COMPLETED; eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); drv->last_scan_cmd = 0; @@ -2132,8 +2401,9 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, break; case NL80211_CMD_SCAN_ABORTED: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted"); - if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) { + if (drv->last_scan_cmd != NL80211_CMD_VENDOR) drv->scan_state = SCAN_ABORTED; + if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) { /* * Need to indicate that scan results are available in * order not to make wpa_supplicant stop its scanning. @@ -2168,7 +2438,13 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, tb[NL80211_ATTR_REQ_IE], tb[NL80211_ATTR_RESP_IE], tb[NL80211_ATTR_TIMED_OUT], - NULL, NULL, NULL, NULL, NULL); + tb[NL80211_ATTR_TIMEOUT_REASON], + NULL, NULL, NULL, + tb[NL80211_ATTR_FILS_KEK], + NULL, + tb[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM], + tb[NL80211_ATTR_PMK], + tb[NL80211_ATTR_PMKID]); break; case NL80211_CMD_CH_SWITCH_NOTIFY: mlme_event_ch_switch(drv, @@ -2200,6 +2476,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, nl80211_cqm_event(drv, tb); break; case NL80211_CMD_REG_CHANGE: + case NL80211_CMD_WIPHY_REG_CHANGE: nl80211_reg_change_event(drv, tb); break; case NL80211_CMD_REG_BEACON_HINT: @@ -2245,6 +2522,12 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, case NL80211_CMD_NEW_PEER_CANDIDATE: nl80211_new_peer_candidate(drv, tb); break; + case NL80211_CMD_PORT_AUTHORIZED: + nl80211_port_authorized(drv, tb); + break; + case NL80211_CMD_STA_OPMODE_CHANGED: + nl80211_sta_opmode_change_event(drv, tb); + break; default: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", cmd); @@ -2259,10 +2542,11 @@ int process_global_event(struct nl_msg *msg, void *arg) struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct wpa_driver_nl80211_data *drv, *tmp; - int ifidx = -1; + int ifidx = -1, wiphy_idx = -1, wiphy_idx_rx = -1; struct i802_bss *bss; u64 wdev_id = 0; int wdev_id_set = 0; + int wiphy_idx_set = 0; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); @@ -2272,13 +2556,19 @@ int process_global_event(struct nl_msg *msg, void *arg) else if (tb[NL80211_ATTR_WDEV]) { wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); wdev_id_set = 1; + } else if (tb[NL80211_ATTR_WIPHY]) { + wiphy_idx_rx = nla_get_u32(tb[NL80211_ATTR_WIPHY]); + wiphy_idx_set = 1; } dl_list_for_each_safe(drv, tmp, &global->interfaces, struct wpa_driver_nl80211_data, list) { for (bss = drv->first_bss; bss; bss = bss->next) { - if ((ifidx == -1 && !wdev_id_set) || + if (wiphy_idx_set) + wiphy_idx = nl80211_get_wiphy_index(bss); + if ((ifidx == -1 && !wiphy_idx_set && !wdev_id_set) || ifidx == bss->ifindex || + (wiphy_idx_set && wiphy_idx == wiphy_idx_rx) || (wdev_id_set && bss->wdev_id_set && wdev_id == bss->wdev_id)) { do_process_drv_event(bss, gnlh->cmd, tb); @@ -2323,6 +2613,9 @@ int process_bss_event(struct nl_msg *msg, void *arg) case NL80211_CMD_UNEXPECTED_4ADDR_FRAME: nl80211_spurious_frame(bss, tb, 1); break; + case NL80211_CMD_EXTERNAL_AUTH: + nl80211_external_auth(bss->drv, tb); + break; default: wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", gnlh->cmd); diff --git a/src/drivers/driver_nl80211_monitor.c b/src/drivers/driver_nl80211_monitor.c index 9376d1143800..f25cd792464a 100644 --- a/src/drivers/driver_nl80211_monitor.c +++ b/src/drivers/driver_nl80211_monitor.c @@ -361,8 +361,17 @@ int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) */ snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss->ifname + 4); } else { + int ret; + /* Non-P2P interface with AP functionality. */ - snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss->ifname); + ret = os_snprintf(buf, IFNAMSIZ, "mon.%s", + drv->first_bss->ifname); + if (ret >= (int) sizeof(buf)) + wpa_printf(MSG_DEBUG, + "nl80211: Monitor interface name has been truncated to %s", + buf); + else if (ret < 0) + return ret; } buf[IFNAMSIZ - 1] = '\0'; diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c index c115b6b31b7d..33a8d359848f 100644 --- a/src/drivers/driver_nl80211_scan.c +++ b/src/drivers/driver_nl80211_scan.c @@ -10,6 +10,7 @@ */ #include "includes.h" +#include <time.h> #include <netlink/genl/genl.h> #include "utils/common.h" @@ -20,6 +21,14 @@ #include "driver_nl80211.h" +#define MAX_NL80211_NOISE_FREQS 50 + +struct nl80211_noise_info { + u32 freq[MAX_NL80211_NOISE_FREQS]; + s8 noise[MAX_NL80211_NOISE_FREQS]; + unsigned int count; +}; + static int get_noise_for_scan_results(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -29,9 +38,10 @@ static int get_noise_for_scan_results(struct nl_msg *msg, void *arg) [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, }; - struct wpa_scan_results *scan_results = arg; - struct wpa_scan_res *scan_res; - size_t i; + struct nl80211_noise_info *info = arg; + + if (info->count >= MAX_NL80211_NOISE_FREQS) + return NL_STOP; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); @@ -55,36 +65,83 @@ static int get_noise_for_scan_results(struct nl_msg *msg, void *arg) if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) return NL_SKIP; - for (i = 0; i < scan_results->num; ++i) { - scan_res = scan_results->res[i]; - if (!scan_res) - continue; - if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != - scan_res->freq) - continue; - if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID)) - continue; - scan_res->noise = (s8) - nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); - scan_res->flags &= ~WPA_SCAN_NOISE_INVALID; - } + info->freq[info->count] = + nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]); + info->noise[info->count] = + (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + info->count++; return NL_SKIP; } static int nl80211_get_noise_for_scan_results( - struct wpa_driver_nl80211_data *drv, - struct wpa_scan_results *scan_res) + struct wpa_driver_nl80211_data *drv, struct nl80211_noise_info *info) { struct nl_msg *msg; + os_memset(info, 0, sizeof(*info)); msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); - return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, - scan_res); + return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, info); } +static int nl80211_abort_scan(struct i802_bss *bss) +{ + int ret; + struct nl_msg *msg; + struct wpa_driver_nl80211_data *drv = bss->drv; + + wpa_printf(MSG_DEBUG, "nl80211: Abort scan"); + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)", + ret, strerror(-ret)); + } + return ret; +} + + +#ifdef CONFIG_DRIVER_NL80211_QCA +static int nl80211_abort_vendor_scan(struct wpa_driver_nl80211_data *drv, + u64 scan_cookie) +{ + struct nl_msg *msg; + struct nlattr *params; + int ret; + + wpa_printf(MSG_DEBUG, "nl80211: Abort vendor scan with cookie 0x%llx", + (long long unsigned int) scan_cookie); + + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR); + if (!msg || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN) || + !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u64(msg, QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE, scan_cookie)) + goto fail; + + nla_nest_end(msg, params); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_INFO, + "nl80211: Aborting vendor scan with cookie 0x%llx failed: ret=%d (%s)", + (long long unsigned int) scan_cookie, ret, + strerror(-ret)); + goto fail; + } + return 0; +fail: + nlmsg_free(msg); + return -1; +} +#endif /* CONFIG_DRIVER_NL80211_QCA */ + + /** * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion * @eloop_ctx: Driver private data @@ -98,7 +155,13 @@ void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) struct wpa_driver_nl80211_data *drv = eloop_ctx; wpa_printf(MSG_DEBUG, "nl80211: Scan timeout - try to abort it"); - if (!wpa_driver_nl80211_abort_scan(drv->first_bss)) +#ifdef CONFIG_DRIVER_NL80211_QCA + if (drv->vendor_scan_cookie && + nl80211_abort_vendor_scan(drv, drv->vendor_scan_cookie) == 0) + return; +#endif /* CONFIG_DRIVER_NL80211_QCA */ + if (!drv->vendor_scan_cookie && + nl80211_abort_scan(drv->first_bss) == 0) return; wpa_printf(MSG_DEBUG, "nl80211: Failed to abort scan"); @@ -206,6 +269,34 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd, } } + if (params->duration) { + if (!(drv->capa.rrm_flags & + WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL) || + nla_put_u16(msg, NL80211_ATTR_MEASUREMENT_DURATION, + params->duration)) + goto fail; + + if (params->duration_mandatory && + nla_put_flag(msg, + NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY)) + goto fail; + } + + if (params->oce_scan) { + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME"); + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP"); + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_OCE_PROBE_REQ_MIN_TX_RATE"); + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION"); + scan_flags |= NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME | + NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP | + NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE | + NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION; + } + if (scan_flags && nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags)) goto fail; @@ -487,6 +578,44 @@ int wpa_driver_nl80211_sched_scan(void *priv, nla_nest_end(msg, match_sets); } + if (params->relative_rssi_set) { + struct nl80211_bss_select_rssi_adjust rssi_adjust; + + os_memset(&rssi_adjust, 0, sizeof(rssi_adjust)); + wpa_printf(MSG_DEBUG, "nl80211: Relative RSSI: %d", + params->relative_rssi); + if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI, + params->relative_rssi)) + goto fail; + + if (params->relative_adjust_rssi) { + int pref_band_set = 1; + + switch (params->relative_adjust_band) { + case WPA_SETBAND_5G: + rssi_adjust.band = NL80211_BAND_5GHZ; + break; + case WPA_SETBAND_2G: + rssi_adjust.band = NL80211_BAND_2GHZ; + break; + default: + pref_band_set = 0; + break; + } + rssi_adjust.delta = params->relative_adjust_rssi; + + if (pref_band_set && + nla_put(msg, NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST, + sizeof(rssi_adjust), &rssi_adjust)) + goto fail; + } + } + + if (params->sched_scan_start_delay && + nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_DELAY, + params->sched_scan_start_delay)) + goto fail; + ret = send_and_recv_msgs(drv, msg, NULL, NULL); /* TODO: if we get an error here, we should fall back to normal scan */ @@ -562,7 +691,9 @@ static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv, } -int bss_info_handler(struct nl_msg *msg, void *arg) +static struct wpa_scan_res * +nl80211_parse_bss_info(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); @@ -579,50 +710,22 @@ int bss_info_handler(struct nl_msg *msg, void *arg) [NL80211_BSS_STATUS] = { .type = NLA_U32 }, [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 }, [NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC }, + [NL80211_BSS_PARENT_TSF] = { .type = NLA_U64 }, + [NL80211_BSS_PARENT_BSSID] = { .type = NLA_UNSPEC }, + [NL80211_BSS_LAST_SEEN_BOOTTIME] = { .type = NLA_U64 }, }; - struct nl80211_bss_info_arg *_arg = arg; - struct wpa_scan_results *res = _arg->res; - struct wpa_scan_res **tmp; struct wpa_scan_res *r; const u8 *ie, *beacon_ie; size_t ie_len, beacon_ie_len; u8 *pos; - size_t i; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (!tb[NL80211_ATTR_BSS]) - return NL_SKIP; + return NULL; if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy)) - return NL_SKIP; - if (bss[NL80211_BSS_STATUS]) { - enum nl80211_bss_status status; - status = nla_get_u32(bss[NL80211_BSS_STATUS]); - if (status == NL80211_BSS_STATUS_ASSOCIATED && - bss[NL80211_BSS_FREQUENCY]) { - _arg->assoc_freq = - nla_get_u32(bss[NL80211_BSS_FREQUENCY]); - wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz", - _arg->assoc_freq); - } - if (status == NL80211_BSS_STATUS_IBSS_JOINED && - bss[NL80211_BSS_FREQUENCY]) { - _arg->ibss_freq = - nla_get_u32(bss[NL80211_BSS_FREQUENCY]); - wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz", - _arg->ibss_freq); - } - if (status == NL80211_BSS_STATUS_ASSOCIATED && - bss[NL80211_BSS_BSSID]) { - os_memcpy(_arg->assoc_bssid, - nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); - wpa_printf(MSG_DEBUG, "nl80211: Associated with " - MACSTR, MAC2STR(_arg->assoc_bssid)); - } - } - if (!res) - return NL_SKIP; + return NULL; if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) { ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]); ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); @@ -638,13 +741,13 @@ int bss_info_handler(struct nl_msg *msg, void *arg) beacon_ie_len = 0; } - if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie, + if (nl80211_scan_filtered(drv, ie ? ie : beacon_ie, ie ? ie_len : beacon_ie_len)) - return NL_SKIP; + return NULL; r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len); if (r == NULL) - return NL_SKIP; + return NULL; if (bss[NL80211_BSS_BSSID]) os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); @@ -673,6 +776,23 @@ int bss_info_handler(struct nl_msg *msg, void *arg) } if (bss[NL80211_BSS_SEEN_MS_AGO]) r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]); + if (bss[NL80211_BSS_LAST_SEEN_BOOTTIME]) { + u64 boottime; + struct timespec ts; + +#ifndef CLOCK_BOOTTIME +#define CLOCK_BOOTTIME 7 +#endif + if (clock_gettime(CLOCK_BOOTTIME, &ts) == 0) { + /* Use more accurate boottime information to update the + * scan result age since the driver reports this and + * CLOCK_BOOTTIME is available. */ + boottime = nla_get_u64( + bss[NL80211_BSS_LAST_SEEN_BOOTTIME]); + r->age = ((u64) ts.tv_sec * 1000000000 + + ts.tv_nsec - boottime) / 1000000; + } + } r->ie_len = ie_len; pos = (u8 *) (r + 1); if (ie) { @@ -695,40 +815,36 @@ int bss_info_handler(struct nl_msg *msg, void *arg) } } - /* - * cfg80211 maintains separate BSS table entries for APs if the same - * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does - * not use frequency as a separate key in the BSS table, so filter out - * duplicated entries. Prefer associated BSS entry in such a case in - * order to get the correct frequency into the BSS table. Similarly, - * prefer newer entries over older. - */ - for (i = 0; i < res->num; i++) { - const u8 *s1, *s2; - if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0) - continue; + if (bss[NL80211_BSS_PARENT_TSF] && bss[NL80211_BSS_PARENT_BSSID]) { + r->parent_tsf = nla_get_u64(bss[NL80211_BSS_PARENT_TSF]); + os_memcpy(r->tsf_bssid, nla_data(bss[NL80211_BSS_PARENT_BSSID]), + ETH_ALEN); + } - s1 = get_ie((u8 *) (res->res[i] + 1), - res->res[i]->ie_len, WLAN_EID_SSID); - s2 = get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID); - if (s1 == NULL || s2 == NULL || s1[1] != s2[1] || - os_memcmp(s1, s2, 2 + s1[1]) != 0) - continue; + return r; +} - /* Same BSSID,SSID was already included in scan results */ - wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result " - "for " MACSTR, MAC2STR(r->bssid)); - if (((r->flags & WPA_SCAN_ASSOCIATED) && - !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) || - r->age < res->res[i]->age) { - os_free(res->res[i]); - res->res[i] = r; - } else - os_free(r); +struct nl80211_bss_info_arg { + struct wpa_driver_nl80211_data *drv; + struct wpa_scan_results *res; +}; + +static int bss_info_handler(struct nl_msg *msg, void *arg) +{ + struct nl80211_bss_info_arg *_arg = arg; + struct wpa_scan_results *res = _arg->res; + struct wpa_scan_res **tmp; + struct wpa_scan_res *r; + + r = nl80211_parse_bss_info(_arg->drv, msg); + if (!r) return NL_SKIP; - } + if (!res) { + os_free(r); + return NL_SKIP; + } tmp = os_realloc_array(res->res, res->num + 1, sizeof(struct wpa_scan_res *)); if (tmp == NULL) { @@ -750,7 +866,32 @@ static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv, "mismatch (" MACSTR ")", MAC2STR(addr)); wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, - WLAN_REASON_PREV_AUTH_NOT_VALID, 1); + WLAN_REASON_PREV_AUTH_NOT_VALID, 1, + NULL); + } +} + + +static void nl80211_check_bss_status(struct wpa_driver_nl80211_data *drv, + struct wpa_scan_res *r) +{ + if (!(r->flags & WPA_SCAN_ASSOCIATED)) + return; + + wpa_printf(MSG_DEBUG, "nl80211: Scan results indicate BSS status with " + MACSTR " as associated", MAC2STR(r->bssid)); + if (is_sta_interface(drv->nlmode) && !drv->associated) { + wpa_printf(MSG_DEBUG, + "nl80211: Local state (not associated) does not match with BSS state"); + clear_state_mismatch(drv, r->bssid); + } else if (is_sta_interface(drv->nlmode) && + os_memcmp(drv->bssid, r->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Local state (associated with " MACSTR + ") does not match with BSS state", + MAC2STR(drv->bssid)); + clear_state_mismatch(drv, r->bssid); + clear_state_mismatch(drv, drv->bssid); } } @@ -760,31 +901,22 @@ static void wpa_driver_nl80211_check_bss_status( { size_t i; - for (i = 0; i < res->num; i++) { - struct wpa_scan_res *r = res->res[i]; + for (i = 0; i < res->num; i++) + nl80211_check_bss_status(drv, res->res[i]); +} - if (r->flags & WPA_SCAN_ASSOCIATED) { - wpa_printf(MSG_DEBUG, "nl80211: Scan results " - "indicate BSS status with " MACSTR - " as associated", - MAC2STR(r->bssid)); - if (is_sta_interface(drv->nlmode) && - !drv->associated) { - wpa_printf(MSG_DEBUG, "nl80211: Local state " - "(not associated) does not match " - "with BSS state"); - clear_state_mismatch(drv, r->bssid); - } else if (is_sta_interface(drv->nlmode) && - os_memcmp(drv->bssid, r->bssid, ETH_ALEN) != - 0) { - wpa_printf(MSG_DEBUG, "nl80211: Local state " - "(associated with " MACSTR ") does " - "not match with BSS state", - MAC2STR(drv->bssid)); - clear_state_mismatch(drv, r->bssid); - clear_state_mismatch(drv, drv->bssid); - } - } + +static void nl80211_update_scan_res_noise(struct wpa_scan_res *res, + struct nl80211_noise_info *info) +{ + unsigned int i; + + for (i = 0; res && i < info->count; i++) { + if ((int) info->freq[i] != res->freq || + !(res->flags & WPA_SCAN_NOISE_INVALID)) + continue; + res->noise = info->noise[i]; + res->flags &= ~WPA_SCAN_NOISE_INVALID; } } @@ -810,9 +942,17 @@ nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) arg.res = res; ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); if (ret == 0) { + struct nl80211_noise_info info; + wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu " "BSSes)", (unsigned long) res->num); - nl80211_get_noise_for_scan_results(drv, res); + if (nl80211_get_noise_for_scan_results(drv, &info) == 0) { + size_t i; + + for (i = 0; i < res->num; ++i) + nl80211_update_scan_res_noise(res->res[i], + &info); + } return res; } wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " @@ -840,45 +980,57 @@ struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv) } -void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv) +struct nl80211_dump_scan_ctx { + struct wpa_driver_nl80211_data *drv; + int idx; +}; + +static int nl80211_dump_scan_handler(struct nl_msg *msg, void *arg) { - struct wpa_scan_results *res; - size_t i; + struct nl80211_dump_scan_ctx *ctx = arg; + struct wpa_scan_res *r; - res = nl80211_get_scan_results(drv); - if (res == NULL) { - wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results"); - return; - } + r = nl80211_parse_bss_info(ctx->drv, msg); + if (!r) + return NL_SKIP; + wpa_printf(MSG_DEBUG, "nl80211: %d " MACSTR " %d%s", + ctx->idx, MAC2STR(r->bssid), r->freq, + r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : ""); + ctx->idx++; + os_free(r); + return NL_SKIP; +} - wpa_printf(MSG_DEBUG, "nl80211: Scan result dump"); - for (i = 0; i < res->num; i++) { - struct wpa_scan_res *r = res->res[i]; - wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s", - (int) i, (int) res->num, MAC2STR(r->bssid), - r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : ""); - } - wpa_scan_results_free(res); +void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + struct nl80211_dump_scan_ctx ctx; + + wpa_printf(MSG_DEBUG, "nl80211: Scan result dump"); + ctx.drv = drv; + ctx.idx = 0; + msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP, NL80211_CMD_GET_SCAN); + if (msg) + send_and_recv_msgs(drv, msg, nl80211_dump_scan_handler, &ctx); } -int wpa_driver_nl80211_abort_scan(void *priv) +int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie) { struct i802_bss *bss = priv; +#ifdef CONFIG_DRIVER_NL80211_QCA struct wpa_driver_nl80211_data *drv = bss->drv; - int ret; - struct nl_msg *msg; - - wpa_printf(MSG_DEBUG, "nl80211: Abort scan"); - msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)", - ret, strerror(-ret)); - } - return ret; + /* + * If scan_cookie is zero, a normal scan through kernel (cfg80211) + * was triggered, hence abort the cfg80211 scan instead of the vendor + * scan. + */ + if (drv->scan_vendor_cmd_avail && scan_cookie) + return nl80211_abort_vendor_scan(drv, scan_cookie); +#endif /* CONFIG_DRIVER_NL80211_QCA */ + return nl80211_abort_scan(bss); } @@ -1015,7 +1167,7 @@ int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, } if (scan_flags && - nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags)) + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS, scan_flags)) goto fail; if (params->p2p_probe) { @@ -1043,6 +1195,14 @@ int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, goto fail; } + if (params->bssid) { + wpa_printf(MSG_DEBUG, "nl80211: Scan for a specific BSSID: " + MACSTR, MAC2STR(params->bssid)); + if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_BSSID, ETH_ALEN, + params->bssid)) + goto fail; + } + nla_nest_end(msg, attr); ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie); @@ -1056,6 +1216,8 @@ int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, drv->vendor_scan_cookie = cookie; drv->scan_state = SCAN_REQUESTED; + /* Pass the cookie to the caller to help distinguish the scans. */ + params->scan_cookie = cookie; wpa_printf(MSG_DEBUG, "nl80211: Vendor scan requested (ret=%d) - scan timeout 30 seconds, scan cookie:0x%llx", diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c index 43d41937d474..a3f0837e1569 100644 --- a/src/drivers/driver_privsep.c +++ b/src/drivers/driver_privsep.c @@ -97,15 +97,31 @@ static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd, return 0; } - + static int wpa_driver_privsep_scan(void *priv, struct wpa_driver_scan_params *params) { struct wpa_driver_privsep_data *drv = priv; - const u8 *ssid = params->ssids[0].ssid; - size_t ssid_len = params->ssids[0].ssid_len; + struct privsep_cmd_scan scan; + size_t i; + wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv); - return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, ssid, ssid_len, + os_memset(&scan, 0, sizeof(scan)); + scan.num_ssids = params->num_ssids; + for (i = 0; i < params->num_ssids; i++) { + if (!params->ssids[i].ssid) + continue; + scan.ssid_lens[i] = params->ssids[i].ssid_len; + os_memcpy(scan.ssids[i], params->ssids[i].ssid, + scan.ssid_lens[i]); + } + + for (i = 0; i < PRIVSEP_MAX_SCAN_FREQS && + params->freqs && params->freqs[i]; i++) + scan.freqs[i] = params->freqs[i]; + scan.num_freqs = i; + + return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, &scan, sizeof(scan), NULL, NULL); } @@ -168,12 +184,15 @@ wpa_driver_privsep_get_scan_results2(void *priv) if (len < 0 || len > 10000 || len > end - pos) break; - r = os_malloc(len); + r = os_memdup(pos, len); if (r == NULL) break; - os_memcpy(r, pos, len); pos += len; - if (sizeof(*r) + r->ie_len > (size_t) len) { + if (sizeof(*r) + r->ie_len + r->beacon_ie_len > (size_t) len) { + wpa_printf(MSG_ERROR, + "privsep: Invalid scan result len (%d + %d + %d > %d)", + (int) sizeof(*r), (int) r->ie_len, + (int) r->beacon_ie_len, len); os_free(r); break; } @@ -234,7 +253,7 @@ static int wpa_driver_privsep_authenticate( __func__, priv, params->freq, MAC2STR(params->bssid), params->auth_alg, params->local_state_change, params->p2p); - buflen = sizeof(*data) + params->ie_len + params->sae_data_len; + buflen = sizeof(*data) + params->ie_len + params->auth_data_len; data = os_zalloc(buflen); if (data == NULL) return -1; @@ -259,8 +278,8 @@ static int wpa_driver_privsep_authenticate( os_memcpy(pos, params->ie, params->ie_len); pos += params->ie_len; } - if (params->sae_data_len) - os_memcpy(pos, params->sae_data, params->sae_data_len); + if (params->auth_data_len) + os_memcpy(pos, params->auth_data, params->auth_data_len); res = wpa_priv_cmd(drv, PRIVSEP_CMD_AUTHENTICATE, data, buflen, NULL, NULL); @@ -464,19 +483,6 @@ static void wpa_driver_privsep_event_pmkid_candidate(void *ctx, u8 *buf, } -static void wpa_driver_privsep_event_stkstart(void *ctx, u8 *buf, size_t len) -{ - union wpa_event_data data; - - if (len != ETH_ALEN) - return; - - os_memset(&data, 0, sizeof(data)); - os_memcpy(data.stkstart.peer, buf, ETH_ALEN); - wpa_supplicant_event(ctx, EVENT_STKSTART, &data); -} - - static void wpa_driver_privsep_event_ft_response(void *ctx, u8 *buf, size_t len) { @@ -570,10 +576,6 @@ static void wpa_driver_privsep_receive(int sock, void *eloop_ctx, wpa_driver_privsep_event_pmkid_candidate(drv->ctx, event_buf, event_len); break; - case PRIVSEP_EVENT_STKSTART: - wpa_driver_privsep_event_stkstart(drv->ctx, event_buf, - event_len); - break; case PRIVSEP_EVENT_FT_RESPONSE: wpa_driver_privsep_event_ft_response(drv->ctx, event_buf, event_len); diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c index 791cd5d435d0..20abaab4cd84 100644 --- a/src/drivers/driver_wext.c +++ b/src/drivers/driver_wext.c @@ -290,15 +290,6 @@ wpa_driver_wext_event_wireless_custom(void *ctx, char *custom) done: os_free(resp_ies); os_free(req_ies); -#ifdef CONFIG_PEERKEY - } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) { - if (hwaddr_aton(custom + 17, data.stkstart.peer)) { - wpa_printf(MSG_DEBUG, "WEXT: unrecognized " - "STKSTART.request '%s'", custom + 17); - return; - } - wpa_supplicant_event(ctx, EVENT_STKSTART, &data); -#endif /* CONFIG_PEERKEY */ } } @@ -362,12 +353,11 @@ static int wpa_driver_wext_event_wireless_assocreqie( wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev, len); os_free(drv->assoc_req_ies); - drv->assoc_req_ies = os_malloc(len); + drv->assoc_req_ies = os_memdup(ev, len); if (drv->assoc_req_ies == NULL) { drv->assoc_req_ies_len = 0; return -1; } - os_memcpy(drv->assoc_req_ies, ev, len); drv->assoc_req_ies_len = len; return 0; @@ -383,12 +373,11 @@ static int wpa_driver_wext_event_wireless_assocrespie( wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev, len); os_free(drv->assoc_resp_ies); - drv->assoc_resp_ies = os_malloc(len); + drv->assoc_resp_ies = os_memdup(ev, len); if (drv->assoc_resp_ies == NULL) { drv->assoc_resp_ies_len = 0; return -1; } - os_memcpy(drv->assoc_resp_ies, ev, len); drv->assoc_resp_ies_len = len; return 0; @@ -472,7 +461,7 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, drv->assoc_resp_ies = NULL; wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); - + } else { wpa_driver_wext_event_assoc_ies(drv); wpa_supplicant_event(drv->ctx, EVENT_ASSOC, @@ -933,7 +922,7 @@ static int wext_add_hostap(struct wpa_driver_wext_data *drv) static void wext_check_hostap(struct wpa_driver_wext_data *drv) { - char buf[200], *pos; + char path[200], buf[200], *pos; ssize_t res; /* @@ -948,9 +937,9 @@ static void wext_check_hostap(struct wpa_driver_wext_data *drv) */ /* First, try to see if driver information is available from sysfs */ - snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/driver", + snprintf(path, sizeof(path), "/sys/class/net/%s/device/driver", drv->ifname); - res = readlink(buf, buf, sizeof(buf) - 1); + res = readlink(path, buf, sizeof(buf) - 1); if (res > 0) { buf[res] = '\0'; pos = strrchr(buf, '/'); @@ -1042,6 +1031,7 @@ void wpa_driver_wext_deinit(void *priv) wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 0); eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx); + eloop_cancel_timeout(wpa_driver_wext_send_rfkill, drv, drv->ctx); /* * Clear possibly configured driver parameters in order to make it @@ -2352,19 +2342,21 @@ static int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv, } -static int wpa_driver_wext_add_pmkid(void *priv, const u8 *bssid, - const u8 *pmkid) +static int wpa_driver_wext_add_pmkid(void *priv, + struct wpa_pmkid_params *params) { struct wpa_driver_wext_data *drv = priv; - return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid); + return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, params->bssid, + params->pmkid); } -static int wpa_driver_wext_remove_pmkid(void *priv, const u8 *bssid, - const u8 *pmkid) +static int wpa_driver_wext_remove_pmkid(void *priv, + struct wpa_pmkid_params *params) { struct wpa_driver_wext_data *drv = priv; - return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid); + return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, params->bssid, + params->pmkid); } @@ -2436,8 +2428,8 @@ static int wpa_driver_wext_signal_poll(void *priv, struct wpa_signal_info *si) struct iwreq iwr; os_memset(si, 0, sizeof(*si)); - si->current_signal = -9999; - si->current_noise = 9999; + si->current_signal = -WPA_INVALID_NOISE; + si->current_noise = WPA_INVALID_NOISE; si->chanwidth = CHAN_WIDTH_UNKNOWN; os_memset(&iwr, 0, sizeof(iwr)); diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c index 422a22064ebe..c7537b7c35eb 100644 --- a/src/drivers/driver_wired.c +++ b/src/drivers/driver_wired.c @@ -12,6 +12,7 @@ #include "common.h" #include "eloop.h" #include "driver.h" +#include "driver_wired_common.h" #include <sys/ioctl.h> #undef IFNAMSIZ @@ -42,20 +43,12 @@ struct ieee8023_hdr { #pragma pack(pop) #endif /* _MSC_VER */ -static const u8 pae_group_addr[ETH_ALEN] = -{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; - struct wpa_driver_wired_data { - char ifname[IFNAMSIZ + 1]; - void *ctx; + struct driver_wired_common_data common; - int sock; /* raw packet socket for driver access */ int dhcp_sock; /* socket for dhcp packets */ int use_pae_group_addr; - - int pf_sock; - int membership, multi, iff_allmulti, iff_up; }; @@ -83,34 +76,6 @@ struct dhcp_message { }; -static int wired_multicast_membership(int sock, int ifindex, - const u8 *addr, int add) -{ -#ifdef __linux__ - struct packet_mreq mreq; - - if (sock < 0) - return -1; - - os_memset(&mreq, 0, sizeof(mreq)); - mreq.mr_ifindex = ifindex; - mreq.mr_type = PACKET_MR_MULTICAST; - mreq.mr_alen = ETH_ALEN; - os_memcpy(mreq.mr_address, addr, ETH_ALEN); - - if (setsockopt(sock, SOL_PACKET, - add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, - &mreq, sizeof(mreq)) < 0) { - wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno)); - return -1; - } - return 0; -#else /* __linux__ */ - return -1; -#endif /* __linux__ */ -} - - #ifdef __linux__ static void handle_data(void *ctx, unsigned char *buf, size_t len) { @@ -131,16 +96,16 @@ static void handle_data(void *ctx, unsigned char *buf, size_t len) hdr = (struct ieee8023_hdr *) buf; switch (ntohs(hdr->ethertype)) { - case ETH_P_PAE: - wpa_printf(MSG_MSGDUMP, "Received EAPOL packet"); - sa = hdr->src; - os_memset(&event, 0, sizeof(event)); - event.new_sta.addr = sa; - wpa_supplicant_event(ctx, EVENT_NEW_STA, &event); + case ETH_P_PAE: + wpa_printf(MSG_MSGDUMP, "Received EAPOL packet"); + sa = hdr->src; + os_memset(&event, 0, sizeof(event)); + event.new_sta.addr = sa; + wpa_supplicant_event(ctx, EVENT_NEW_STA, &event); - pos = (u8 *) (hdr + 1); - left = len - sizeof(*hdr); - drv_event_eapol_rx(ctx, sa, pos, left); + pos = (u8 *) (hdr + 1); + left = len - sizeof(*hdr); + drv_event_eapol_rx(ctx, sa, pos, left); break; default: @@ -208,21 +173,22 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) struct sockaddr_in addr2; int n = 1; - drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); - if (drv->sock < 0) { + drv->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); + if (drv->common.sock < 0) { wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s", strerror(errno)); return -1; } - if (eloop_register_read_sock(drv->sock, handle_read, drv->ctx, NULL)) { + if (eloop_register_read_sock(drv->common.sock, handle_read, + drv->common.ctx, NULL)) { wpa_printf(MSG_INFO, "Could not register read socket"); return -1; } os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); - if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { + os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name)); + if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) { wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", strerror(errno)); return -1; @@ -234,13 +200,14 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", addr.sll_ifindex); - if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + if (bind(drv->common.sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) + { wpa_printf(MSG_ERROR, "bind: %s", strerror(errno)); return -1; } /* filter multicast address */ - if (wired_multicast_membership(drv->sock, ifr.ifr_ifindex, + if (wired_multicast_membership(drv->common.sock, ifr.ifr_ifindex, pae_group_addr, 1) < 0) { wpa_printf(MSG_ERROR, "wired: Failed to add multicast group " "membership"); @@ -248,8 +215,8 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) } os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); - if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) { + os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name)); + if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) { wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s", strerror(errno)); return -1; @@ -269,8 +236,8 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) return -1; } - if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, drv->ctx, - NULL)) { + if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, + drv->common.ctx, NULL)) { wpa_printf(MSG_INFO, "Could not register read socket"); return -1; } @@ -294,7 +261,7 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) } os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->ifname, IFNAMSIZ); + os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->common.ifname, IFNAMSIZ); if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE, (char *) &ifr, sizeof(ifr)) < 0) { wpa_printf(MSG_ERROR, @@ -343,7 +310,7 @@ static int wired_send_eapol(void *priv, const u8 *addr, pos = (u8 *) (hdr + 1); os_memcpy(pos, data, data_len); - res = send(drv->sock, (u8 *) hdr, len, 0); + res = send(drv->common.sock, (u8 *) hdr, len, 0); os_free(hdr); if (res < 0) { @@ -368,8 +335,9 @@ static void * wired_driver_hapd_init(struct hostapd_data *hapd, return NULL; } - drv->ctx = hapd; - os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname)); + drv->common.ctx = hapd; + os_strlcpy(drv->common.ifname, params->ifname, + sizeof(drv->common.ifname)); drv->use_pae_group_addr = params->use_pae_group_addr; if (wired_init_sockets(drv, params->own_addr)) { @@ -385,9 +353,9 @@ static void wired_driver_hapd_deinit(void *priv) { struct wpa_driver_wired_data *drv = priv; - if (drv->sock >= 0) { - eloop_unregister_read_sock(drv->sock); - close(drv->sock); + if (drv->common.sock >= 0) { + eloop_unregister_read_sock(drv->common.sock); + close(drv->common.sock); } if (drv->dhcp_sock >= 0) { @@ -399,227 +367,18 @@ static void wired_driver_hapd_deinit(void *priv) } -static int wpa_driver_wired_get_ssid(void *priv, u8 *ssid) -{ - ssid[0] = 0; - return 0; -} - - -static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid) -{ - /* Report PAE group address as the "BSSID" for wired connection. */ - os_memcpy(bssid, pae_group_addr, ETH_ALEN); - return 0; -} - - -static int wpa_driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa) -{ - os_memset(capa, 0, sizeof(*capa)); - capa->flags = WPA_DRIVER_FLAGS_WIRED; - return 0; -} - - -static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags) -{ - struct ifreq ifr; - int s; - - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) { - wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); - return -1; - } - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s", - strerror(errno)); - close(s); - return -1; - } - close(s); - *flags = ifr.ifr_flags & 0xffff; - return 0; -} - - -static int wpa_driver_wired_set_ifflags(const char *ifname, int flags) -{ - struct ifreq ifr; - int s; - - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) { - wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); - return -1; - } - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_flags = flags & 0xffff; - if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s", - strerror(errno)); - close(s); - return -1; - } - close(s); - return 0; -} - - -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) -static int wpa_driver_wired_get_ifstatus(const char *ifname, int *status) -{ - struct ifmediareq ifmr; - int s; - - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) { - wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); - return -1; - } - - os_memset(&ifmr, 0, sizeof(ifmr)); - os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ); - if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s", - strerror(errno)); - close(s); - return -1; - } - close(s); - *status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) == - (IFM_ACTIVE | IFM_AVALID); - - return 0; -} -#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ - - -static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) -{ - struct ifreq ifr; - int s; - -#ifdef __sun__ - return -1; -#endif /* __sun__ */ - - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) { - wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); - return -1; - } - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); -#ifdef __linux__ - ifr.ifr_hwaddr.sa_family = AF_UNSPEC; - os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); -#endif /* __linux__ */ -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) - { - struct sockaddr_dl *dlp; - dlp = (struct sockaddr_dl *) &ifr.ifr_addr; - dlp->sdl_len = sizeof(struct sockaddr_dl); - dlp->sdl_family = AF_LINK; - dlp->sdl_index = 0; - dlp->sdl_nlen = 0; - dlp->sdl_alen = ETH_ALEN; - dlp->sdl_slen = 0; - os_memcpy(LLADDR(dlp), addr, ETH_ALEN); - } -#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ -#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) - { - struct sockaddr *sap; - sap = (struct sockaddr *) &ifr.ifr_addr; - sap->sa_len = sizeof(struct sockaddr); - sap->sa_family = AF_UNSPEC; - os_memcpy(sap->sa_data, addr, ETH_ALEN); - } -#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */ - - if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s", - strerror(errno)); - close(s); - return -1; - } - close(s); - return 0; -} - - static void * wpa_driver_wired_init(void *ctx, const char *ifname) { struct wpa_driver_wired_data *drv; - int flags; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->ctx = ctx; - -#ifdef __linux__ - drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0); - if (drv->pf_sock < 0) - wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno)); -#else /* __linux__ */ - drv->pf_sock = -1; -#endif /* __linux__ */ - - if (wpa_driver_wired_get_ifflags(ifname, &flags) == 0 && - !(flags & IFF_UP) && - wpa_driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) { - drv->iff_up = 1; - } - if (wired_multicast_membership(drv->pf_sock, - if_nametoindex(drv->ifname), - pae_group_addr, 1) == 0) { - wpa_printf(MSG_DEBUG, "%s: Added multicast membership with " - "packet socket", __func__); - drv->membership = 1; - } else if (wpa_driver_wired_multi(ifname, pae_group_addr, 1) == 0) { - wpa_printf(MSG_DEBUG, "%s: Added multicast membership with " - "SIOCADDMULTI", __func__); - drv->multi = 1; - } else if (wpa_driver_wired_get_ifflags(ifname, &flags) < 0) { - wpa_printf(MSG_INFO, "%s: Could not get interface " - "flags", __func__); - os_free(drv); - return NULL; - } else if (flags & IFF_ALLMULTI) { - wpa_printf(MSG_DEBUG, "%s: Interface is already configured " - "for multicast", __func__); - } else if (wpa_driver_wired_set_ifflags(ifname, - flags | IFF_ALLMULTI) < 0) { - wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", - __func__); + if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) { os_free(drv); return NULL; - } else { - wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", - __func__); - drv->iff_allmulti = 1; - } -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) - { - int status; - wpa_printf(MSG_DEBUG, "%s: waiting for link to become active", - __func__); - while (wpa_driver_wired_get_ifstatus(ifname, &status) == 0 && - status == 0) - sleep(1); } -#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ return drv; } @@ -628,41 +387,8 @@ static void * wpa_driver_wired_init(void *ctx, const char *ifname) static void wpa_driver_wired_deinit(void *priv) { struct wpa_driver_wired_data *drv = priv; - int flags; - - if (drv->membership && - wired_multicast_membership(drv->pf_sock, - if_nametoindex(drv->ifname), - pae_group_addr, 0) < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast " - "group (PACKET)", __func__); - } - - if (drv->multi && - wpa_driver_wired_multi(drv->ifname, pae_group_addr, 0) < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast " - "group (SIOCDELMULTI)", __func__); - } - - if (drv->iff_allmulti && - (wpa_driver_wired_get_ifflags(drv->ifname, &flags) < 0 || - wpa_driver_wired_set_ifflags(drv->ifname, - flags & ~IFF_ALLMULTI) < 0)) { - wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode", - __func__); - } - - if (drv->iff_up && - wpa_driver_wired_get_ifflags(drv->ifname, &flags) == 0 && - (flags & IFF_UP) && - wpa_driver_wired_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down", - __func__); - } - - if (drv->pf_sock != -1) - close(drv->pf_sock); + driver_wired_deinit_common(&drv->common); os_free(drv); } @@ -673,9 +399,9 @@ const struct wpa_driver_ops wpa_driver_wired_ops = { .hapd_init = wired_driver_hapd_init, .hapd_deinit = wired_driver_hapd_deinit, .hapd_send_eapol = wired_send_eapol, - .get_ssid = wpa_driver_wired_get_ssid, - .get_bssid = wpa_driver_wired_get_bssid, - .get_capa = wpa_driver_wired_get_capa, + .get_ssid = driver_wired_get_ssid, + .get_bssid = driver_wired_get_bssid, + .get_capa = driver_wired_get_capa, .init = wpa_driver_wired_init, .deinit = wpa_driver_wired_deinit, }; diff --git a/src/drivers/driver_wired_common.c b/src/drivers/driver_wired_common.c new file mode 100644 index 000000000000..a860b1c7db35 --- /dev/null +++ b/src/drivers/driver_wired_common.c @@ -0,0 +1,322 @@ +/* + * Common functions for Wired Ethernet driver interfaces + * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004, Gunter Burchardt <tira@isx.de> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "driver.h" +#include "driver_wired_common.h" + +#include <sys/ioctl.h> +#include <net/if.h> +#ifdef __linux__ +#include <netpacket/packet.h> +#include <net/if_arp.h> +#include <net/if.h> +#endif /* __linux__ */ +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +#include <net/if_dl.h> +#include <net/if_media.h> +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */ +#ifdef __sun__ +#include <sys/sockio.h> +#endif /* __sun__ */ + + +static int driver_wired_get_ifflags(const char *ifname, int *flags) +{ + struct ifreq ifr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s", + strerror(errno)); + close(s); + return -1; + } + close(s); + *flags = ifr.ifr_flags & 0xffff; + return 0; +} + + +static int driver_wired_set_ifflags(const char *ifname, int flags) +{ + struct ifreq ifr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags = flags & 0xffff; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s", + strerror(errno)); + close(s); + return -1; + } + close(s); + return 0; +} + + +static int driver_wired_multi(const char *ifname, const u8 *addr, int add) +{ + struct ifreq ifr; + int s; + +#ifdef __sun__ + return -1; +#endif /* __sun__ */ + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); +#ifdef __linux__ + ifr.ifr_hwaddr.sa_family = AF_UNSPEC; + os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); +#endif /* __linux__ */ +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) + { + struct sockaddr_dl *dlp; + + dlp = (struct sockaddr_dl *) &ifr.ifr_addr; + dlp->sdl_len = sizeof(struct sockaddr_dl); + dlp->sdl_family = AF_LINK; + dlp->sdl_index = 0; + dlp->sdl_nlen = 0; + dlp->sdl_alen = ETH_ALEN; + dlp->sdl_slen = 0; + os_memcpy(LLADDR(dlp), addr, ETH_ALEN); + } +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ +#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) + { + struct sockaddr *sap; + + sap = (struct sockaddr *) &ifr.ifr_addr; + sap->sa_len = sizeof(struct sockaddr); + sap->sa_family = AF_UNSPEC; + os_memcpy(sap->sa_data, addr, ETH_ALEN); + } +#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */ + + if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s", + strerror(errno)); + close(s); + return -1; + } + close(s); + return 0; +} + + +int wired_multicast_membership(int sock, int ifindex, const u8 *addr, int add) +{ +#ifdef __linux__ + struct packet_mreq mreq; + + if (sock < 0) + return -1; + + os_memset(&mreq, 0, sizeof(mreq)); + mreq.mr_ifindex = ifindex; + mreq.mr_type = PACKET_MR_MULTICAST; + mreq.mr_alen = ETH_ALEN; + os_memcpy(mreq.mr_address, addr, ETH_ALEN); + + if (setsockopt(sock, SOL_PACKET, + add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno)); + return -1; + } + return 0; +#else /* __linux__ */ + return -1; +#endif /* __linux__ */ +} + + +int driver_wired_get_ssid(void *priv, u8 *ssid) +{ + ssid[0] = 0; + return 0; +} + + +int driver_wired_get_bssid(void *priv, u8 *bssid) +{ + /* Report PAE group address as the "BSSID" for wired connection. */ + os_memcpy(bssid, pae_group_addr, ETH_ALEN); + return 0; +} + + +int driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + os_memset(capa, 0, sizeof(*capa)); + capa->flags = WPA_DRIVER_FLAGS_WIRED; + return 0; +} + + +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +static int driver_wired_get_ifstatus(const char *ifname, int *status) +{ + struct ifmediareq ifmr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); + return -1; + } + + os_memset(&ifmr, 0, sizeof(ifmr)); + os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ); + if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s", + strerror(errno)); + close(s); + return -1; + } + close(s); + *status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID); + + return 0; +} +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ + + +int driver_wired_init_common(struct driver_wired_common_data *common, + const char *ifname, void *ctx) +{ + int flags; + + os_strlcpy(common->ifname, ifname, sizeof(common->ifname)); + common->ctx = ctx; + +#ifdef __linux__ + common->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0); + if (common->pf_sock < 0) + wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno)); +#else /* __linux__ */ + common->pf_sock = -1; +#endif /* __linux__ */ + + if (driver_wired_get_ifflags(ifname, &flags) == 0 && + !(flags & IFF_UP) && + driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) + common->iff_up = 1; + + if (wired_multicast_membership(common->pf_sock, + if_nametoindex(common->ifname), + pae_group_addr, 1) == 0) { + wpa_printf(MSG_DEBUG, + "%s: Added multicast membership with packet socket", + __func__); + common->membership = 1; + } else if (driver_wired_multi(ifname, pae_group_addr, 1) == 0) { + wpa_printf(MSG_DEBUG, + "%s: Added multicast membership with SIOCADDMULTI", + __func__); + common->multi = 1; + } else if (driver_wired_get_ifflags(ifname, &flags) < 0) { + wpa_printf(MSG_INFO, "%s: Could not get interface flags", + __func__); + return -1; + } else if (flags & IFF_ALLMULTI) { + wpa_printf(MSG_DEBUG, + "%s: Interface is already configured for multicast", + __func__); + } else if (driver_wired_set_ifflags(ifname, + flags | IFF_ALLMULTI) < 0) { + wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", __func__); + return -1; + } else { + wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", __func__); + common->iff_allmulti = 1; + } +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) + { + int status; + + wpa_printf(MSG_DEBUG, "%s: waiting for link to become active", + __func__); + while (driver_wired_get_ifstatus(ifname, &status) == 0 && + status == 0) + sleep(1); + } +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ + + return 0; +} + + +void driver_wired_deinit_common(struct driver_wired_common_data *common) +{ + int flags; + + if (common->membership && + wired_multicast_membership(common->pf_sock, + if_nametoindex(common->ifname), + pae_group_addr, 0) < 0) { + wpa_printf(MSG_DEBUG, + "%s: Failed to remove PAE multicast group (PACKET)", + __func__); + } + + if (common->multi && + driver_wired_multi(common->ifname, pae_group_addr, 0) < 0) { + wpa_printf(MSG_DEBUG, + "%s: Failed to remove PAE multicast group (SIOCDELMULTI)", + __func__); + } + + if (common->iff_allmulti && + (driver_wired_get_ifflags(common->ifname, &flags) < 0 || + driver_wired_set_ifflags(common->ifname, + flags & ~IFF_ALLMULTI) < 0)) { + wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode", + __func__); + } + + if (common->iff_up && + driver_wired_get_ifflags(common->ifname, &flags) == 0 && + (flags & IFF_UP) && + driver_wired_set_ifflags(common->ifname, flags & ~IFF_UP) < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down", + __func__); + } + + if (common->pf_sock != -1) + close(common->pf_sock); +} diff --git a/src/drivers/driver_wired_common.h b/src/drivers/driver_wired_common.h new file mode 100644 index 000000000000..2bb0710bb275 --- /dev/null +++ b/src/drivers/driver_wired_common.h @@ -0,0 +1,34 @@ +/* + * Common definitions for Wired Ethernet driver interfaces + * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004, Gunter Burchardt <tira@isx.de> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DRIVER_WIRED_COMMON_H +#define DRIVER_WIRED_COMMON_H + +struct driver_wired_common_data { + char ifname[IFNAMSIZ + 1]; + void *ctx; + + int sock; /* raw packet socket for driver access */ + int pf_sock; + int membership, multi, iff_allmulti, iff_up; +}; + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + +int wired_multicast_membership(int sock, int ifindex, const u8 *addr, int add); +int driver_wired_get_ssid(void *priv, u8 *ssid); +int driver_wired_get_bssid(void *priv, u8 *bssid); +int driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa); + +int driver_wired_init_common(struct driver_wired_common_data *common, + const char *ifname, void *ctx); +void driver_wired_deinit_common(struct driver_wired_common_data *common); + +#endif /* DRIVER_WIRED_COMMON_H */ diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c index 00773a7113f6..e95df6ddb20a 100644 --- a/src/drivers/drivers.c +++ b/src/drivers/drivers.c @@ -34,6 +34,9 @@ const struct wpa_driver_ops *const wpa_drivers[] = #ifdef CONFIG_DRIVER_WIRED &wpa_driver_wired_ops, #endif /* CONFIG_DRIVER_WIRED */ +#ifdef CONFIG_DRIVER_MACSEC_LINUX + &wpa_driver_macsec_linux_ops, +#endif /* CONFIG_DRIVER_MACSEC_LINUX */ #ifdef CONFIG_DRIVER_MACSEC_QCA &wpa_driver_macsec_qca_ops, #endif /* CONFIG_DRIVER_MACSEC_QCA */ diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak index c6d3f8181808..1496b47d0ad5 100644 --- a/src/drivers/drivers.mak +++ b/src/drivers/drivers.mak @@ -15,11 +15,24 @@ DRV_AP_LIBS = ifdef CONFIG_DRIVER_WIRED DRV_CFLAGS += -DCONFIG_DRIVER_WIRED DRV_OBJS += ../src/drivers/driver_wired.o +NEED_DRV_WIRED_COMMON=1 +endif + +ifdef CONFIG_DRIVER_MACSEC_LINUX +DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX +DRV_OBJS += ../src/drivers/driver_macsec_linux.o +NEED_DRV_WIRED_COMMON=1 +CONFIG_LIBNL3_ROUTE=y endif ifdef CONFIG_DRIVER_MACSEC_QCA DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_QCA DRV_OBJS += ../src/drivers/driver_macsec_qca.o +NEED_DRV_WIRED_COMMON=1 +endif + +ifdef NEED_DRV_WIRED_COMMON +DRV_OBJS += ../src/drivers/driver_wired_common.o endif ifdef CONFIG_DRIVER_NL80211 diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk index c6fe4c20c561..cd25133af808 100644 --- a/src/drivers/drivers.mk +++ b/src/drivers/drivers.mk @@ -15,6 +15,18 @@ DRV_AP_LIBS = ifdef CONFIG_DRIVER_WIRED DRV_CFLAGS += -DCONFIG_DRIVER_WIRED DRV_OBJS += src/drivers/driver_wired.c +NEED_DRV_WIRED_COMMON=1 +endif + +ifdef CONFIG_DRIVER_MACSEC_LINUX +DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX +DRV_OBJS += src/drivers/driver_macsec_linux.c +NEED_DRV_WIRED_COMMON=1 +CONFIG_LIBNL3_ROUTE=y +endif + +ifdef NEED_DRV_WIRED_COMMON +DRV_OBJS += src/drivers/driver_wired_common.c endif ifdef CONFIG_DRIVER_NL80211 diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h index 220694151434..1766a12b231c 100644 --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -10,7 +10,8 @@ * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com> * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com> * Copyright 2008 Colin McCabe <colin@cozybit.com> - * Copyright 2015 Intel Deutschland GmbH + * Copyright 2015-2017 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -48,6 +49,7 @@ #define NL80211_MULTICAST_GROUP_REG "regulatory" #define NL80211_MULTICAST_GROUP_MLME "mlme" #define NL80211_MULTICAST_GROUP_VENDOR "vendor" +#define NL80211_MULTICAST_GROUP_NAN "nan" #define NL80211_MULTICAST_GROUP_TESTMODE "testmode" /** @@ -172,6 +174,67 @@ */ /** + * DOC: WPA/WPA2 EAPOL handshake offload + * + * By setting @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK flag drivers + * can indicate they support offloading EAPOL handshakes for WPA/WPA2 + * preshared key authentication. In %NL80211_CMD_CONNECT the preshared + * key should be specified using %NL80211_ATTR_PMK. Drivers supporting + * this offload may reject the %NL80211_CMD_CONNECT when no preshared + * key material is provided, for example when that driver does not + * support setting the temporal keys through %CMD_NEW_KEY. + * + * Similarly @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X flag can be + * set by drivers indicating offload support of the PTK/GTK EAPOL + * handshakes during 802.1X authentication. In order to use the offload + * the %NL80211_CMD_CONNECT should have %NL80211_ATTR_WANT_1X_4WAY_HS + * attribute flag. Drivers supporting this offload may reject the + * %NL80211_CMD_CONNECT when the attribute flag is not present. + * + * For 802.1X the PMK or PMK-R0 are set by providing %NL80211_ATTR_PMK + * using %NL80211_CMD_SET_PMK. For offloaded FT support also + * %NL80211_ATTR_PMKR0_NAME must be provided. + */ + +/** + * DOC: FILS shared key authentication offload + * + * FILS shared key authentication offload can be advertized by drivers by + * setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support + * FILS shared key authentication offload should be able to construct the + * authentication and association frames for FILS shared key authentication and + * eventually do a key derivation as per IEEE 802.11ai. The below additional + * parameters should be given to driver in %NL80211_CMD_CONNECT and/or in + * %NL80211_CMD_UPDATE_CONNECT_PARAMS. + * %NL80211_ATTR_FILS_ERP_USERNAME - used to construct keyname_nai + * %NL80211_ATTR_FILS_ERP_REALM - used to construct keyname_nai + * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used to construct erp message + * %NL80211_ATTR_FILS_ERP_RRK - used to generate the rIK and rMSK + * rIK should be used to generate an authentication tag on the ERP message and + * rMSK should be used to derive a PMKSA. + * rIK, rMSK should be generated and keyname_nai, sequence number should be used + * as specified in IETF RFC 6696. + * + * When FILS shared key authentication is completed, driver needs to provide the + * below additional parameters to userspace, which can be either after setting + * up a connection or after roaming. + * %NL80211_ATTR_FILS_KEK - used for key renewal + * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used in further EAP-RP exchanges + * %NL80211_ATTR_PMKID - used to identify the PMKSA used/generated + * %Nl80211_ATTR_PMK - used to update PMKSA cache in userspace + * The PMKSA can be maintained in userspace persistently so that it can be used + * later after reboots or wifi turn off/on also. + * + * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS + * capable AP supporting PMK caching. It specifies the scope within which the + * PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and + * %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based + * on FILS cache identifier. Additionally %NL80211_ATTR_PMK is used with + * %NL80211_SET_PMKSA to specify the PMK corresponding to a PMKSA for driver to + * use in a FILS shared key connection with PMKSA caching. + */ + +/** * enum nl80211_commands - supported nl80211 commands * * @NL80211_CMD_UNSPEC: unspecified command to catch errors @@ -322,7 +385,7 @@ * @NL80211_CMD_GET_SCAN: get scan results * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the - * probe requests at CCK rate or not. %NL80211_ATTR_MAC can be used to + * probe requests at CCK rate or not. %NL80211_ATTR_BSSID can be used to * specify a BSSID to scan for; if not included, the wildcard BSSID will * be used. * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to @@ -350,7 +413,9 @@ * are used. Extra IEs can also be passed from the userspace by * using the %NL80211_ATTR_IE attribute. The first cycle of the * scheduled scan can be delayed by %NL80211_ATTR_SCHED_SCAN_DELAY - * is supplied. + * is supplied. If the device supports multiple concurrent scheduled + * scans, it will allow such when the caller provides the flag attribute + * %NL80211_ATTR_SCHED_SCAN_MULTI to indicate user-space support for it. * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if * scheduled scan is not running. The caller may assume that as soon * as the call returns, it is safe to start a new scheduled scan again. @@ -369,10 +434,18 @@ * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) * - * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry, using %NL80211_ATTR_MAC - * (for the BSSID) and %NL80211_ATTR_PMKID. + * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry using %NL80211_ATTR_MAC + * (for the BSSID), %NL80211_ATTR_PMKID, and optionally %NL80211_ATTR_PMK + * (PMK is used for PTKSA derivation in case of FILS shared key offload) or + * using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID, + * %NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS + * authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier + * advertized by a FILS capable AP identifying the scope of PMKSA in an + * ESS. * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC - * (for the BSSID) and %NL80211_ATTR_PMKID. + * (for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID, + * %NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS + * authentication. * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries. * * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain @@ -472,7 +545,8 @@ * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP, * %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, - * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, %NL80211_ATTR_MAC_HINT, and + * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, + * %NL80211_ATTR_CONTROL_PORT_OVER_NL80211, %NL80211_ATTR_MAC_HINT, and * %NL80211_ATTR_WIPHY_FREQ_HINT. * If included, %NL80211_ATTR_MAC and %NL80211_ATTR_WIPHY_FREQ are * restrictions on BSS selection, i.e., they effectively prevent roaming @@ -499,8 +573,14 @@ * authentication/association or not receiving a response from the AP. * Non-zero %NL80211_ATTR_STATUS_CODE value is indicated in that case as * well to remain backwards compatible. - * @NL80211_CMD_ROAM: request that the card roam (currently not implemented), - * sent as an event when the card/driver roamed by itself. + * When establishing a security association, drivers that support 4 way + * handshake offload should send %NL80211_CMD_PORT_AUTHORIZED event when + * the 4 way handshake is completed successfully. + * @NL80211_CMD_ROAM: Notification indicating the card/driver roamed by itself. + * When a security association was established with the new AP (e.g. if + * the FT protocol was used for roaming or the driver completed the 4 way + * handshake), this event should be followed by an + * %NL80211_CMD_PORT_AUTHORIZED event. * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify * userspace that a connection was dropped by the AP or due to other * reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and @@ -599,6 +679,20 @@ * * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface. * + * @NL80211_CMD_SET_MULTICAST_TO_UNICAST: Configure if this AP should perform + * multicast to unicast conversion. When enabled, all multicast packets + * with ethertype ARP, IPv4 or IPv6 (possibly within an 802.1Q header) + * will be sent out to each station once with the destination (multicast) + * MAC address replaced by the station's MAC address. Note that this may + * break certain expectations of the receiver, e.g. the ability to drop + * unicast IP packets encapsulated in multicast L2 frames, or the ability + * to not send destination unreachable messages in such cases. + * This can only be toggled per BSS. Configure this on an interface of + * type %NL80211_IFTYPE_AP. It applies to all its VLAN interfaces + * (%NL80211_IFTYPE_AP_VLAN), except for those in 4addr (WDS) mode. + * If %NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED is not present with this + * command, the feature is disabled. + * * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial * mesh config parameters may be given. * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the @@ -838,6 +932,107 @@ * not running. The driver indicates the status of the scan through * cfg80211_scan_done(). * + * @NL80211_CMD_START_NAN: Start NAN operation, identified by its + * %NL80211_ATTR_WDEV interface. This interface must have been + * previously created with %NL80211_CMD_NEW_INTERFACE. After it + * has been started, the NAN interface will create or join a + * cluster. This command must have a valid + * %NL80211_ATTR_NAN_MASTER_PREF attribute and optional + * %NL80211_ATTR_BANDS attributes. If %NL80211_ATTR_BANDS is + * omitted or set to 0, it means don't-care and the device will + * decide what to use. After this command NAN functions can be + * added. + * @NL80211_CMD_STOP_NAN: Stop the NAN operation, identified by + * its %NL80211_ATTR_WDEV interface. + * @NL80211_CMD_ADD_NAN_FUNCTION: Add a NAN function. The function is defined + * with %NL80211_ATTR_NAN_FUNC nested attribute. When called, this + * operation returns the strictly positive and unique instance id + * (%NL80211_ATTR_NAN_FUNC_INST_ID) and a cookie (%NL80211_ATTR_COOKIE) + * of the function upon success. + * Since instance ID's can be re-used, this cookie is the right + * way to identify the function. This will avoid races when a termination + * event is handled by the user space after it has already added a new + * function that got the same instance id from the kernel as the one + * which just terminated. + * This cookie may be used in NAN events even before the command + * returns, so userspace shouldn't process NAN events until it processes + * the response to this command. + * Look at %NL80211_ATTR_SOCKET_OWNER as well. + * @NL80211_CMD_DEL_NAN_FUNCTION: Delete a NAN function by cookie. + * This command is also used as a notification sent when a NAN function is + * terminated. This will contain a %NL80211_ATTR_NAN_FUNC_INST_ID + * and %NL80211_ATTR_COOKIE attributes. + * @NL80211_CMD_CHANGE_NAN_CONFIG: Change current NAN + * configuration. NAN must be operational (%NL80211_CMD_START_NAN + * was executed). It must contain at least one of the following + * attributes: %NL80211_ATTR_NAN_MASTER_PREF, + * %NL80211_ATTR_BANDS. If %NL80211_ATTR_BANDS is omitted, the + * current configuration is not changed. If it is present but + * set to zero, the configuration is changed to don't-care + * (i.e. the device can decide what to do). + * @NL80211_CMD_NAN_FUNC_MATCH: Notification sent when a match is reported. + * This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and + * %NL80211_ATTR_COOKIE. + * + * @NL80211_CMD_UPDATE_CONNECT_PARAMS: Update one or more connect parameters + * for subsequent roaming cases if the driver or firmware uses internal + * BSS selection. This command can be issued only while connected and it + * does not result in a change for the current association. Currently, + * only the %NL80211_ATTR_IE data is used and updated with this command. + * + * @NL80211_CMD_SET_PMK: For offloaded 4-Way handshake, set the PMK or PMK-R0 + * for the given authenticator address (specified with %NL80211_ATTR_MAC). + * When %NL80211_ATTR_PMKR0_NAME is set, %NL80211_ATTR_PMK specifies the + * PMK-R0, otherwise it specifies the PMK. + * @NL80211_CMD_DEL_PMK: For offloaded 4-Way handshake, delete the previously + * configured PMK for the authenticator address identified by + * %NL80211_ATTR_MAC. + * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates that the 4 way + * handshake was completed successfully by the driver. The BSSID is + * specified with %NL80211_ATTR_MAC. Drivers that support 4 way handshake + * offload should send this event after indicating 802.11 association with + * %NL80211_CMD_CONNECT or %NL80211_CMD_ROAM. If the 4 way handshake failed + * %NL80211_CMD_DISCONNECT should be indicated instead. + * + * @NL80211_CMD_CONTROL_PORT_FRAME: Control Port (e.g. PAE) frame TX request + * and RX notification. This command is used both as a request to transmit + * a control port frame and as a notification that a control port frame + * has been received. %NL80211_ATTR_FRAME is used to specify the + * frame contents. The frame is the raw EAPoL data, without ethernet or + * 802.11 headers. + * When used as an event indication %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, + * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT and %NL80211_ATTR_MAC are added + * indicating the protocol type of the received frame; whether the frame + * was received unencrypted and the MAC address of the peer respectively. + * + * @NL80211_CMD_RELOAD_REGDB: Request that the regdb firmware file is reloaded. + * + * @NL80211_CMD_EXTERNAL_AUTH: This interface is exclusively defined for host + * drivers that do not define separate commands for authentication and + * association, but rely on user space for the authentication to happen. + * This interface acts both as the event request (driver to user space) + * to trigger the authentication and command response (userspace to + * driver) to indicate the authentication status. + * + * User space uses the %NL80211_CMD_CONNECT command to the host driver to + * trigger a connection. The host driver selects a BSS and further uses + * this interface to offload only the authentication part to the user + * space. Authentication frames are passed between the driver and user + * space through the %NL80211_CMD_FRAME interface. Host driver proceeds + * further with the association after getting successful authentication + * status. User space indicates the authentication status through + * %NL80211_ATTR_STATUS_CODE attribute in %NL80211_CMD_EXTERNAL_AUTH + * command interface. + * + * Host driver reports this status on an authentication failure to the + * user space through the connect result as the user space would have + * initiated the connection through the connect request. + * + * @NL80211_CMD_STA_OPMODE_CHANGED: An event that notify station's + * ht opmode or vht opmode changes using any of %NL80211_ATTR_SMPS_MODE, + * %NL80211_ATTR_CHANNEL_WIDTH,%NL80211_ATTR_NSS attributes with its + * address(specified in %NL80211_ATTR_MAC). + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1026,6 +1221,30 @@ enum nl80211_commands { NL80211_CMD_ABORT_SCAN, + NL80211_CMD_START_NAN, + NL80211_CMD_STOP_NAN, + NL80211_CMD_ADD_NAN_FUNCTION, + NL80211_CMD_DEL_NAN_FUNCTION, + NL80211_CMD_CHANGE_NAN_CONFIG, + NL80211_CMD_NAN_MATCH, + + NL80211_CMD_SET_MULTICAST_TO_UNICAST, + + NL80211_CMD_UPDATE_CONNECT_PARAMS, + + NL80211_CMD_SET_PMK, + NL80211_CMD_DEL_PMK, + + NL80211_CMD_PORT_AUTHORIZED, + + NL80211_CMD_RELOAD_REGDB, + + NL80211_CMD_EXTERNAL_AUTH, + + NL80211_CMD_STA_OPMODE_CHANGED, + + NL80211_CMD_CONTROL_PORT_FRAME, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1248,8 +1467,12 @@ enum nl80211_commands { * * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is * used for the association (&enum nl80211_mfp, represented as a u32); - * this attribute can be used - * with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests + * this attribute can be used with %NL80211_CMD_ASSOCIATE and + * %NL80211_CMD_CONNECT requests. %NL80211_MFP_OPTIONAL is not allowed for + * %NL80211_CMD_ASSOCIATE since user space SME is expected and hence, it + * must have decided whether to use management frame protection or not. + * Setting %NL80211_MFP_OPTIONAL with a %NL80211_CMD_CONNECT request will + * let the driver (or the firmware) decide whether to use MFP or not. * * @NL80211_ATTR_STA_FLAGS2: Attribute containing a * &struct nl80211_sta_flag_update. @@ -1269,6 +1492,15 @@ enum nl80211_commands { * @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom * ethertype frames used for key negotiation must not be encrypted. + * @NL80211_ATTR_CONTROL_PORT_OVER_NL80211: A flag indicating whether control + * port frames (e.g. of type given in %NL80211_ATTR_CONTROL_PORT_ETHERTYPE) + * will be sent directly to the network interface or sent via the NL80211 + * socket. If this attribute is missing, then legacy behavior of sending + * control port frames directly to the network interface is used. If the + * flag is included, then control port frames are sent over NL80211 instead + * using %CMD_CONTROL_PORT_FRAME. If control port routing over NL80211 is + * to be used then userspace must also use the %NL80211_ATTR_SOCKET_OWNER + * flag. * * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver. * We recommend using nested, driver-specific attributes within this. @@ -1343,7 +1575,13 @@ enum nl80211_commands { * enum nl80211_band value is used as the index (nla_type() of the nested * data. If a band is not included, it will be configured to allow all * rates based on negotiated supported rates information. This attribute - * is used with %NL80211_CMD_SET_TX_BITRATE_MASK. + * is used with %NL80211_CMD_SET_TX_BITRATE_MASK and with starting AP, + * and joining mesh networks (not IBSS yet). In the later case, it must + * specify just a single bitrate, which is to be used for the beacon. + * The driver must also specify support for this with the extended + * features NL80211_EXT_FEATURE_BEACON_RATE_LEGACY, + * NL80211_EXT_FEATURE_BEACON_RATE_HT and + * NL80211_EXT_FEATURE_BEACON_RATE_VHT. * * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain * at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME. @@ -1589,8 +1827,16 @@ enum nl80211_commands { * the connection request from a station. nl80211_connect_failed_reason * enum has different reasons of connection failure. * - * @NL80211_ATTR_SAE_DATA: SAE elements in Authentication frames. This starts - * with the Authentication transaction sequence number field. + * @NL80211_ATTR_AUTH_DATA: Fields and elements in Authentication frames. + * This contains the authentication frame body (non-IE and IE data), + * excluding the Authentication algorithm number, i.e., starting at the + * Authentication transaction sequence number field. It is used with + * authentication algorithms that need special fields to be added into + * the frames (SAE and FILS). Currently, only the SAE cases use the + * initial two fields (Authentication transaction sequence number and + * Status code). However, those fields are included in the attribute data + * for all authentication algorithms to keep the attribute definition + * consistent. * * @NL80211_ATTR_VHT_CAPABILITY: VHT Capability information element (from * association request when used with NL80211_CMD_NEW_STATION) @@ -1691,7 +1937,9 @@ enum nl80211_commands { * * @NL80211_ATTR_OPMODE_NOTIF: Operating mode field from Operating Mode * Notification Element based on association request when used with - * %NL80211_CMD_NEW_STATION; u8 attribute. + * %NL80211_CMD_NEW_STATION or %NL80211_CMD_SET_STATION (only when + * %NL80211_FEATURE_FULL_AP_CLIENT_STATE is supported, or with TDLS); + * u8 attribute. * * @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if * %NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet) @@ -1733,6 +1981,19 @@ enum nl80211_commands { * regulatory indoor configuration would be owned by the netlink socket * that configured the indoor setting, and the indoor operation would be * cleared when the socket is closed. + * If set during NAN interface creation, the interface will be destroyed + * if the socket is closed just like any other interface. Moreover, NAN + * notifications will be sent in unicast to that socket. Without this + * attribute, the notifications will be sent to the %NL80211_MCGRP_NAN + * multicast group. + * If set during %NL80211_CMD_ASSOCIATE or %NL80211_CMD_CONNECT the + * station will deauthenticate when the socket is closed. + * If set during %NL80211_CMD_JOIN_IBSS the IBSS will be automatically + * torn down when the socket is closed. + * If set during %NL80211_CMD_JOIN_MESH the mesh setup will be + * automatically torn down when the socket is closed. + * If set during %NL80211_CMD_START_AP the AP will be automatically + * disabled when the socket is closed. * * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is * the TDLS link initiator. @@ -1867,6 +2128,119 @@ enum nl80211_commands { * @NL80211_ATTR_MESH_PEER_AID: Association ID for the mesh peer (u16). This is * used to pull the stored data for mesh peer in power save state. * + * @NL80211_ATTR_NAN_MASTER_PREF: the master preference to be used by + * %NL80211_CMD_START_NAN and optionally with + * %NL80211_CMD_CHANGE_NAN_CONFIG. Its type is u8 and it can't be 0. + * Also, values 1 and 255 are reserved for certification purposes and + * should not be used during a normal device operation. + * @NL80211_ATTR_BANDS: operating bands configuration. This is a u32 + * bitmask of BIT(NL80211_BAND_*) as described in %enum + * nl80211_band. For instance, for NL80211_BAND_2GHZ, bit 0 + * would be set. This attribute is used with + * %NL80211_CMD_START_NAN and %NL80211_CMD_CHANGE_NAN_CONFIG, and + * it is optional. If no bands are set, it means don't-care and + * the device will decide what to use. + * @NL80211_ATTR_NAN_FUNC: a function that can be added to NAN. See + * &enum nl80211_nan_func_attributes for description of this nested + * attribute. + * @NL80211_ATTR_NAN_MATCH: used to report a match. This is a nested attribute. + * See &enum nl80211_nan_match_attributes. + * @NL80211_ATTR_FILS_KEK: KEK for FILS (Re)Association Request/Response frame + * protection. + * @NL80211_ATTR_FILS_NONCES: Nonces (part of AAD) for FILS (Re)Association + * Request/Response frame protection. This attribute contains the 16 octet + * STA Nonce followed by 16 octets of AP Nonce. + * + * @NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED: Indicates whether or not multicast + * packets should be send out as unicast to all stations (flag attribute). + * + * @NL80211_ATTR_BSSID: The BSSID of the AP. Note that %NL80211_ATTR_MAC is also + * used in various commands/events for specifying the BSSID. + * + * @NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI: Relative RSSI threshold by which + * other BSSs has to be better or slightly worse than the current + * connected BSS so that they get reported to user space. + * This will give an opportunity to userspace to consider connecting to + * other matching BSSs which have better or slightly worse RSSI than + * the current connected BSS by using an offloaded operation to avoid + * unnecessary wakeups. + * + * @NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST: When present the RSSI level for BSSs in + * the specified band is to be adjusted before doing + * %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparision to figure out + * better BSSs. The attribute value is a packed structure + * value as specified by &struct nl80211_bss_select_rssi_adjust. + * + * @NL80211_ATTR_TIMEOUT_REASON: The reason for which an operation timed out. + * u32 attribute with an &enum nl80211_timeout_reason value. This is used, + * e.g., with %NL80211_CMD_CONNECT event. + * + * @NL80211_ATTR_FILS_ERP_USERNAME: EAP Re-authentication Protocol (ERP) + * username part of NAI used to refer keys rRK and rIK. This is used with + * %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_ERP_REALM: EAP Re-authentication Protocol (ERP) realm part + * of NAI specifying the domain name of the ER server. This is used with + * %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM: Unsigned 16-bit ERP next sequence number + * to use in ERP messages. This is used in generating the FILS wrapped data + * for FILS authentication and is used with %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_ERP_RRK: ERP re-authentication Root Key (rRK) for the + * NAI specified by %NL80211_ATTR_FILS_ERP_USERNAME and + * %NL80211_ATTR_FILS_ERP_REALM. This is used for generating rIK and rMSK + * from successful FILS authentication and is used with + * %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP + * identifying the scope of PMKSAs. This is used with + * @NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA. + * + * @NL80211_ATTR_PMK: attribute for passing PMK key material. Used with + * %NL80211_CMD_SET_PMKSA for the PMKSA identified by %NL80211_ATTR_PMKID. + * For %NL80211_CMD_CONNECT it is used to provide PSK for offloading 4-way + * handshake for WPA/WPA2-PSK networks. For 802.1X authentication it is + * used with %NL80211_CMD_SET_PMK. For offloaded FT support this attribute + * specifies the PMK-R0 if NL80211_ATTR_PMKR0_NAME is included as well. + * + * @NL80211_ATTR_SCHED_SCAN_MULTI: flag attribute which user-space shall use to + * indicate that it supports multiple active scheduled scan requests. + * @NL80211_ATTR_SCHED_SCAN_MAX_REQS: indicates maximum number of scheduled + * scan request that may be active for the device (u32). + * + * @NL80211_ATTR_WANT_1X_4WAY_HS: flag attribute which user-space can include + * in %NL80211_CMD_CONNECT to indicate that for 802.1X authentication it + * wants to use the supported offload of the 4-way handshake. + * @NL80211_ATTR_PMKR0_NAME: PMK-R0 Name for offloaded FT. + * @NL80211_ATTR_PORT_AUTHORIZED: (reserved) + * + * @NL80211_ATTR_EXTERNAL_AUTH_ACTION: Identify the requested external + * authentication operation (u32 attribute with an + * &enum nl80211_external_auth_action value). This is used with the + * %NL80211_CMD_EXTERNAL_AUTH request event. + * @NL80211_ATTR_EXTERNAL_AUTH_SUPPORT: Flag attribute indicating that the user + * space supports external authentication. This attribute shall be used + * only with %NL80211_CMD_CONNECT request. The driver may offload + * authentication processing to user space if this capability is indicated + * in NL80211_CMD_CONNECT requests from the user space. + * + * @NL80211_ATTR_NSS: Station's New/updated RX_NSS value notified using this + * u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED. + * + * @NL80211_ATTR_TXQ_STATS: TXQ statistics (nested attribute, see &enum + * nl80211_txq_stats) + * @NL80211_ATTR_TXQ_LIMIT: Total packet limit for the TXQ queues for this phy. + * The smaller of this and the memory limit is enforced. + * @NL80211_ATTR_TXQ_MEMORY_LIMIT: Total memory memory limit (in bytes) for the + * TXQ queues for this phy. The smaller of this and the packet limit is + * enforced. + * @NL80211_ATTR_TXQ_QUANTUM: TXQ scheduler quantum (bytes). Number of bytes + * a flow is assigned on each round of the DRR scheduler. + * @NL80211_ATTR_HE_CAPABILITY: HE Capability information element (from + * association request when used with NL80211_CMD_NEW_STATION). Can be set + * only if %NL80211_STA_FLAG_WME is set. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2125,7 +2499,7 @@ enum nl80211_attrs { NL80211_ATTR_CONN_FAILED_REASON, - NL80211_ATTR_SAE_DATA, + NL80211_ATTR_AUTH_DATA, NL80211_ATTR_VHT_CAPABILITY, @@ -2261,6 +2635,53 @@ enum nl80211_attrs { NL80211_ATTR_MESH_PEER_AID, + NL80211_ATTR_NAN_MASTER_PREF, + NL80211_ATTR_BANDS, + NL80211_ATTR_NAN_FUNC, + NL80211_ATTR_NAN_MATCH, + + NL80211_ATTR_FILS_KEK, + NL80211_ATTR_FILS_NONCES, + + NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED, + + NL80211_ATTR_BSSID, + + NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI, + NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST, + + NL80211_ATTR_TIMEOUT_REASON, + + NL80211_ATTR_FILS_ERP_USERNAME, + NL80211_ATTR_FILS_ERP_REALM, + NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, + NL80211_ATTR_FILS_ERP_RRK, + NL80211_ATTR_FILS_CACHE_ID, + + NL80211_ATTR_PMK, + + NL80211_ATTR_SCHED_SCAN_MULTI, + NL80211_ATTR_SCHED_SCAN_MAX_REQS, + + NL80211_ATTR_WANT_1X_4WAY_HS, + NL80211_ATTR_PMKR0_NAME, + NL80211_ATTR_PORT_AUTHORIZED, + + NL80211_ATTR_EXTERNAL_AUTH_ACTION, + NL80211_ATTR_EXTERNAL_AUTH_SUPPORT, + + NL80211_ATTR_NSS, + NL80211_ATTR_ACK_SIGNAL, + + NL80211_ATTR_CONTROL_PORT_OVER_NL80211, + + NL80211_ATTR_TXQ_STATS, + NL80211_ATTR_TXQ_LIMIT, + NL80211_ATTR_TXQ_MEMORY_LIMIT, + NL80211_ATTR_TXQ_QUANTUM, + + NL80211_ATTR_HE_CAPABILITY, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2272,6 +2693,7 @@ enum nl80211_attrs { #define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION #define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG #define NL80211_ATTR_IFACE_SOCKET_OWNER NL80211_ATTR_SOCKET_OWNER +#define NL80211_ATTR_SAE_DATA NL80211_ATTR_AUTH_DATA /* * Allow user space programs to use #ifdef on new attributes by defining them @@ -2299,6 +2721,8 @@ enum nl80211_attrs { #define NL80211_ATTR_KEYS NL80211_ATTR_KEYS #define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS +#define NL80211_WIPHY_NAME_MAXLEN 64 + #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_HT_RATES 77 #define NL80211_MAX_SUPP_REG_RULES 64 @@ -2307,7 +2731,8 @@ enum nl80211_attrs { #define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 #define NL80211_HT_CAPABILITY_LEN 26 #define NL80211_VHT_CAPABILITY_LEN 12 - +#define NL80211_HE_MIN_CAPABILITY_LEN 16 +#define NL80211_HE_MAX_CAPABILITY_LEN 51 #define NL80211_MAX_NR_CIPHER_SUITES 5 #define NL80211_MAX_NR_AKM_SUITES 2 @@ -2339,6 +2764,7 @@ enum nl80211_attrs { * commands to create and destroy one * @NL80211_IF_TYPE_OCB: Outside Context of a BSS * This mode corresponds to the MIB variable dot11OCBActivated=true + * @NL80211_IFTYPE_NAN: NAN device interface type (not a netdev) * @NL80211_IFTYPE_MAX: highest interface type number currently defined * @NUM_NL80211_IFTYPES: number of defined interface types * @@ -2359,6 +2785,7 @@ enum nl80211_iftype { NL80211_IFTYPE_P2P_GO, NL80211_IFTYPE_P2P_DEVICE, NL80211_IFTYPE_OCB, + NL80211_IFTYPE_NAN, /* keep last */ NUM_NL80211_IFTYPES, @@ -2433,6 +2860,38 @@ struct nl80211_sta_flag_update { } __attribute__((packed)); /** + * enum nl80211_he_gi - HE guard interval + * @NL80211_RATE_INFO_HE_GI_0_8: 0.8 usec + * @NL80211_RATE_INFO_HE_GI_1_6: 1.6 usec + * @NL80211_RATE_INFO_HE_GI_3_2: 3.2 usec + */ +enum nl80211_he_gi { + NL80211_RATE_INFO_HE_GI_0_8, + NL80211_RATE_INFO_HE_GI_1_6, + NL80211_RATE_INFO_HE_GI_3_2, +}; + +/** + * enum nl80211_he_ru_alloc - HE RU allocation values + * @NL80211_RATE_INFO_HE_RU_ALLOC_26: 26-tone RU allocation + * @NL80211_RATE_INFO_HE_RU_ALLOC_52: 52-tone RU allocation + * @NL80211_RATE_INFO_HE_RU_ALLOC_106: 106-tone RU allocation + * @NL80211_RATE_INFO_HE_RU_ALLOC_242: 242-tone RU allocation + * @NL80211_RATE_INFO_HE_RU_ALLOC_484: 484-tone RU allocation + * @NL80211_RATE_INFO_HE_RU_ALLOC_996: 996-tone RU allocation + * @NL80211_RATE_INFO_HE_RU_ALLOC_2x996: 2x996-tone RU allocation + */ +enum nl80211_he_ru_alloc { + NL80211_RATE_INFO_HE_RU_ALLOC_26, + NL80211_RATE_INFO_HE_RU_ALLOC_52, + NL80211_RATE_INFO_HE_RU_ALLOC_106, + NL80211_RATE_INFO_HE_RU_ALLOC_242, + NL80211_RATE_INFO_HE_RU_ALLOC_484, + NL80211_RATE_INFO_HE_RU_ALLOC_996, + NL80211_RATE_INFO_HE_RU_ALLOC_2x996, +}; + +/** * enum nl80211_rate_info - bitrate information * * These attribute types are used with %NL80211_STA_INFO_TXRATE @@ -2464,6 +2923,13 @@ struct nl80211_sta_flag_update { * @NL80211_RATE_INFO_5_MHZ_WIDTH: 5 MHz width - note that this is * a legacy rate and will be reported as the actual bitrate, i.e. * a quarter of the base (20 MHz) rate + * @NL80211_RATE_INFO_HE_MCS: HE MCS index (u8, 0-11) + * @NL80211_RATE_INFO_HE_NSS: HE NSS value (u8, 1-8) + * @NL80211_RATE_INFO_HE_GI: HE guard interval identifier + * (u8, see &enum nl80211_he_gi) + * @NL80211_RATE_INFO_HE_DCM: HE DCM value (u8, 0/1) + * @NL80211_RATE_INFO_RU_ALLOC: HE RU allocation, if not present then + * non-OFDMA was used (u8, see &enum nl80211_he_ru_alloc) * @__NL80211_RATE_INFO_AFTER_LAST: internal use */ enum nl80211_rate_info { @@ -2480,6 +2946,11 @@ enum nl80211_rate_info { NL80211_RATE_INFO_160_MHZ_WIDTH, NL80211_RATE_INFO_10_MHZ_WIDTH, NL80211_RATE_INFO_5_MHZ_WIDTH, + NL80211_RATE_INFO_HE_MCS, + NL80211_RATE_INFO_HE_NSS, + NL80211_RATE_INFO_HE_GI, + NL80211_RATE_INFO_HE_DCM, + NL80211_RATE_INFO_HE_RU_ALLOC, /* keep last */ __NL80211_RATE_INFO_AFTER_LAST, @@ -2578,6 +3049,8 @@ enum nl80211_sta_bss_param { * @NL80211_STA_INFO_RX_DURATION: aggregate PPDU duration for all frames * received from the station (u64, usec) * @NL80211_STA_INFO_PAD: attribute used for padding for 64-bit alignment + * @NL80211_STA_INFO_ACK_SIGNAL: signal strength of the last ACK frame(u8, dBm) + * @NL80211_STA_INFO_ACK_SIGNAL_AVG: avg signal strength of ACK frames (s8, dBm) * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -2616,12 +3089,18 @@ enum nl80211_sta_info { NL80211_STA_INFO_TID_STATS, NL80211_STA_INFO_RX_DURATION, NL80211_STA_INFO_PAD, + NL80211_STA_INFO_ACK_SIGNAL, + NL80211_STA_INFO_ACK_SIGNAL_AVG, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1 }; +/* we renamed this - stay compatible */ +#define NL80211_STA_INFO_DATA_ACK_SIGNAL_AVG NL80211_STA_INFO_ACK_SIGNAL_AVG + + /** * enum nl80211_tid_stats - per TID statistics attributes * @__NL80211_TID_STATS_INVALID: attribute number 0 is reserved @@ -2633,6 +3112,7 @@ enum nl80211_sta_info { * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted * MSDUs (u64) * @NL80211_TID_STATS_PAD: attribute used for padding for 64-bit alignment + * @NL80211_TID_STATS_TXQ_STATS: TXQ stats (nested attribute) * @NUM_NL80211_TID_STATS: number of attributes here * @NL80211_TID_STATS_MAX: highest numbered attribute here */ @@ -2643,6 +3123,7 @@ enum nl80211_tid_stats { NL80211_TID_STATS_TX_MSDU_RETRIES, NL80211_TID_STATS_TX_MSDU_FAILED, NL80211_TID_STATS_PAD, + NL80211_TID_STATS_TXQ_STATS, /* keep last */ NUM_NL80211_TID_STATS, @@ -2650,6 +3131,44 @@ enum nl80211_tid_stats { }; /** + * enum nl80211_txq_stats - per TXQ statistics attributes + * @__NL80211_TXQ_STATS_INVALID: attribute number 0 is reserved + * @NUM_NL80211_TXQ_STATS: number of attributes here + * @NL80211_TXQ_STATS_BACKLOG_BYTES: number of bytes currently backlogged + * @NL80211_TXQ_STATS_BACKLOG_PACKETS: number of packets currently + * backlogged + * @NL80211_TXQ_STATS_FLOWS: total number of new flows seen + * @NL80211_TXQ_STATS_DROPS: total number of packet drops + * @NL80211_TXQ_STATS_ECN_MARKS: total number of packet ECN marks + * @NL80211_TXQ_STATS_OVERLIMIT: number of drops due to queue space overflow + * @NL80211_TXQ_STATS_OVERMEMORY: number of drops due to memory limit overflow + * (only for per-phy stats) + * @NL80211_TXQ_STATS_COLLISIONS: number of hash collisions + * @NL80211_TXQ_STATS_TX_BYTES: total number of bytes dequeued from TXQ + * @NL80211_TXQ_STATS_TX_PACKETS: total number of packets dequeued from TXQ + * @NL80211_TXQ_STATS_MAX_FLOWS: number of flow buckets for PHY + * @NL80211_TXQ_STATS_MAX: highest numbered attribute here + */ +enum nl80211_txq_stats { + __NL80211_TXQ_STATS_INVALID, + NL80211_TXQ_STATS_BACKLOG_BYTES, + NL80211_TXQ_STATS_BACKLOG_PACKETS, + NL80211_TXQ_STATS_FLOWS, + NL80211_TXQ_STATS_DROPS, + NL80211_TXQ_STATS_ECN_MARKS, + NL80211_TXQ_STATS_OVERLIMIT, + NL80211_TXQ_STATS_OVERMEMORY, + NL80211_TXQ_STATS_COLLISIONS, + NL80211_TXQ_STATS_TX_BYTES, + NL80211_TXQ_STATS_TX_PACKETS, + NL80211_TXQ_STATS_MAX_FLOWS, + + /* keep last */ + NUM_NL80211_TXQ_STATS, + NL80211_TXQ_STATS_MAX = NUM_NL80211_TXQ_STATS - 1 +}; + +/** * enum nl80211_mpath_flags - nl80211 mesh path flags * * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active @@ -2701,6 +3220,38 @@ enum nl80211_mpath_info { }; /** + * enum nl80211_band_iftype_attr - Interface type data attributes + * + * @__NL80211_BAND_IFTYPE_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_BAND_IFTYPE_ATTR_IFTYPES: nested attribute containing a flag attribute + * for each interface type that supports the band data + * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC: HE MAC capabilities as in HE + * capabilities IE + * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY: HE PHY capabilities as in HE + * capabilities IE + * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET: HE supported NSS/MCS as in HE + * capabilities IE + * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE: HE PPE thresholds information as + * defined in HE capabilities IE + * @NL80211_BAND_IFTYPE_ATTR_MAX: highest band HE capability attribute currently + * defined + * @__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST: internal use + */ +enum nl80211_band_iftype_attr { + __NL80211_BAND_IFTYPE_ATTR_INVALID, + + NL80211_BAND_IFTYPE_ATTR_IFTYPES, + NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC, + NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY, + NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET, + NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE, + + /* keep last */ + __NL80211_BAND_IFTYPE_ATTR_AFTER_LAST, + NL80211_BAND_IFTYPE_ATTR_MAX = __NL80211_BAND_IFTYPE_ATTR_AFTER_LAST - 1 +}; + +/** * enum nl80211_band_attr - band attributes * @__NL80211_BAND_ATTR_INVALID: attribute number 0 is reserved * @NL80211_BAND_ATTR_FREQS: supported frequencies in this band, @@ -2715,6 +3266,8 @@ enum nl80211_mpath_info { * @NL80211_BAND_ATTR_VHT_MCS_SET: 32-byte attribute containing the MCS set as * defined in 802.11ac * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE + * @NL80211_BAND_ATTR_IFTYPE_DATA: nested array attribute, with each entry using + * attributes from &enum nl80211_band_iftype_attr * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined * @__NL80211_BAND_ATTR_AFTER_LAST: internal use */ @@ -2730,6 +3283,7 @@ enum nl80211_band_attr { NL80211_BAND_ATTR_VHT_MCS_SET, NL80211_BAND_ATTR_VHT_CAPA, + NL80211_BAND_ATTR_IFTYPE_DATA, /* keep last */ __NL80211_BAND_ATTR_AFTER_LAST, @@ -2739,6 +3293,29 @@ enum nl80211_band_attr { #define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA /** + * enum nl80211_wmm_rule - regulatory wmm rule + * + * @__NL80211_WMMR_INVALID: attribute number 0 is reserved + * @NL80211_WMMR_CW_MIN: Minimum contention window slot. + * @NL80211_WMMR_CW_MAX: Maximum contention window slot. + * @NL80211_WMMR_AIFSN: Arbitration Inter Frame Space. + * @NL80211_WMMR_TXOP: Maximum allowed tx operation time. + * @nl80211_WMMR_MAX: highest possible wmm rule. + * @__NL80211_WMMR_LAST: Internal use. + */ +enum nl80211_wmm_rule { + __NL80211_WMMR_INVALID, + NL80211_WMMR_CW_MIN, + NL80211_WMMR_CW_MAX, + NL80211_WMMR_AIFSN, + NL80211_WMMR_TXOP, + + /* keep last */ + __NL80211_WMMR_LAST, + NL80211_WMMR_MAX = __NL80211_WMMR_LAST - 1 +}; + +/** * enum nl80211_frequency_attr - frequency attributes * @__NL80211_FREQUENCY_ATTR_INVALID: attribute number 0 is reserved * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz @@ -2787,6 +3364,9 @@ enum nl80211_band_attr { * on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_WMM: this channel has wmm limitations. + * This is a nested attribute that contains the wmm limitation per AC. + * (see &enum nl80211_wmm_rule) * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use @@ -2815,6 +3395,7 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_IR_CONCURRENT, NL80211_FREQUENCY_ATTR_NO_20MHZ, NL80211_FREQUENCY_ATTR_NO_10MHZ, + NL80211_FREQUENCY_ATTR_WMM, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, @@ -2942,6 +3523,7 @@ enum nl80211_reg_rule_attr { * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching, * only report BSS with matching SSID. + * (This cannot be used together with BSSID.) * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a * BSS in scan results. Filtering is turned off if not specified. Note that * if this attribute is in a match set of its own, then it is treated as @@ -2950,6 +3532,15 @@ enum nl80211_reg_rule_attr { * how this API was implemented in the past. Also, due to the same problem, * the only way to create a matchset with only an RSSI filter (with this * attribute) is if there's only a single matchset with the RSSI attribute. + * @NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI: Flag indicating whether + * %NL80211_SCHED_SCAN_MATCH_ATTR_RSSI to be used as absolute RSSI or + * relative to current bss's RSSI. + * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST: When present the RSSI level for + * BSS-es in the specified band is to be adjusted before doing + * RSSI-based BSS selection. The attribute value is a packed structure + * value as specified by &struct nl80211_bss_select_rssi_adjust. + * @NL80211_SCHED_SCAN_MATCH_ATTR_BSSID: BSSID to be used for matching + * (this cannot be used together with SSID). * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter * attribute number currently defined * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use @@ -2959,6 +3550,9 @@ enum nl80211_sched_scan_match_attr { NL80211_SCHED_SCAN_MATCH_ATTR_SSID, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, + NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI, + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST, + NL80211_SCHED_SCAN_MATCH_ATTR_BSSID, /* keep last */ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, @@ -2985,7 +3579,7 @@ enum nl80211_sched_scan_match_attr { * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated * base on contiguous rules and wider channels will be allowed to cross * multiple contiguous/overlapping frequency ranges. - * @NL80211_RRF_IR_CONCURRENT: See &NL80211_FREQUENCY_ATTR_IR_CONCURRENT + * @NL80211_RRF_IR_CONCURRENT: See %NL80211_FREQUENCY_ATTR_IR_CONCURRENT * @NL80211_RRF_NO_HT40MINUS: channels can't be used in HT40- operation * @NL80211_RRF_NO_HT40PLUS: channels can't be used in HT40+ operation * @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed @@ -3528,6 +4122,9 @@ enum nl80211_bss_scan_width { * @NL80211_BSS_PARENT_BSSID. (u64). * @NL80211_BSS_PARENT_BSSID: the BSS according to which @NL80211_BSS_PARENT_TSF * is set. + * @NL80211_BSS_CHAIN_SIGNAL: per-chain signal strength of last BSS update. + * Contains a nested array of signal strength attributes (u8, dBm), + * using the nesting index as the antenna number. * @__NL80211_BSS_AFTER_LAST: internal * @NL80211_BSS_MAX: highest BSS attribute */ @@ -3551,6 +4148,7 @@ enum nl80211_bss { NL80211_BSS_PAD, NL80211_BSS_PARENT_TSF, NL80211_BSS_PARENT_BSSID, + NL80211_BSS_CHAIN_SIGNAL, /* keep last */ __NL80211_BSS_AFTER_LAST, @@ -3583,6 +4181,9 @@ enum nl80211_bss_status { * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r) * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP) * @NL80211_AUTHTYPE_SAE: Simultaneous authentication of equals + * @NL80211_AUTHTYPE_FILS_SK: Fast Initial Link Setup shared key + * @NL80211_AUTHTYPE_FILS_SK_PFS: Fast Initial Link Setup shared key with PFS + * @NL80211_AUTHTYPE_FILS_PK: Fast Initial Link Setup public key * @__NL80211_AUTHTYPE_NUM: internal * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by @@ -3595,6 +4196,9 @@ enum nl80211_auth_type { NL80211_AUTHTYPE_FT, NL80211_AUTHTYPE_NETWORK_EAP, NL80211_AUTHTYPE_SAE, + NL80211_AUTHTYPE_FILS_SK, + NL80211_AUTHTYPE_FILS_SK_PFS, + NL80211_AUTHTYPE_FILS_PK, /* keep last */ __NL80211_AUTHTYPE_NUM, @@ -3621,10 +4225,12 @@ enum nl80211_key_type { * enum nl80211_mfp - Management frame protection state * @NL80211_MFP_NO: Management frame protection not used * @NL80211_MFP_REQUIRED: Management frame protection required + * @NL80211_MFP_OPTIONAL: Management frame protection is optional */ enum nl80211_mfp { NL80211_MFP_NO, NL80211_MFP_REQUIRED, + NL80211_MFP_OPTIONAL, }; enum nl80211_wpa_versions { @@ -3735,7 +4341,7 @@ enum nl80211_txrate_gi { * enum nl80211_band - Frequency band * @NL80211_BAND_2GHZ: 2.4 GHz ISM band * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz) - * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz) + * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 69.12 GHz) * @NUM_NL80211_BANDS: number of bands, avoid using this in userspace * since newer kernel versions may support more bands */ @@ -3762,7 +4368,10 @@ enum nl80211_ps_state { * @__NL80211_ATTR_CQM_INVALID: invalid * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies * the threshold for the RSSI level at which an event will be sent. Zero - * to disable. + * to disable. Alternatively, if %NL80211_EXT_FEATURE_CQM_RSSI_LIST is + * set, multiple values can be supplied as a low-to-high sorted array of + * threshold values in dBm. Events will be sent when the RSSI value + * crosses any of the thresholds. * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies * the minimum amount the RSSI level must change after an event before a * new event may be issued (to reduce effects of RSSI oscillation). @@ -3782,6 +4391,8 @@ enum nl80211_ps_state { * %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting. * @NL80211_ATTR_CQM_BEACON_LOSS_EVENT: flag attribute that's set in a beacon * loss event + * @NL80211_ATTR_CQM_RSSI_LEVEL: the RSSI value in dBm that triggered the + * RSSI threshold event. * @__NL80211_ATTR_CQM_AFTER_LAST: internal * @NL80211_ATTR_CQM_MAX: highest key attribute */ @@ -3795,6 +4406,7 @@ enum nl80211_attr_cqm { NL80211_ATTR_CQM_TXE_PKTS, NL80211_ATTR_CQM_TXE_INTVL, NL80211_ATTR_CQM_BEACON_LOSS_EVENT, + NL80211_ATTR_CQM_RSSI_LEVEL, /* keep last */ __NL80211_ATTR_CQM_AFTER_LAST, @@ -4203,6 +4815,9 @@ enum nl80211_iface_limit_attrs { * of supported channel widths for radar detection. * @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap * of supported regulatory regions for radar detection. + * @NL80211_IFACE_COMB_BI_MIN_GCD: u32 attribute specifying the minimum GCD of + * different beacon intervals supported by all the interface combinations + * in this group (if not present, all beacon intervals be identical). * @NUM_NL80211_IFACE_COMB: number of attributes * @MAX_NL80211_IFACE_COMB: highest attribute number * @@ -4210,8 +4825,8 @@ enum nl80211_iface_limit_attrs { * limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2 * => allows an AP and a STA that must match BIs * - * numbers = [ #{AP, P2P-GO} <= 8 ], channels = 1, max = 8 - * => allows 8 of AP/GO + * numbers = [ #{AP, P2P-GO} <= 8 ], BI min gcd, channels = 1, max = 8, + * => allows 8 of AP/GO that can have BI gcd >= min gcd * * numbers = [ #{STA} <= 2 ], channels = 2, max = 2 * => allows two STAs on different channels @@ -4237,6 +4852,7 @@ enum nl80211_if_combination_attrs { NL80211_IFACE_COMB_NUM_CHANNELS, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, NL80211_IFACE_COMB_RADAR_DETECT_REGIONS, + NL80211_IFACE_COMB_BI_MIN_GCD, /* keep last */ NUM_NL80211_IFACE_COMB, @@ -4551,6 +5167,64 @@ enum nl80211_feature_flags { * (if available). * @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of * channel dwell time. + * @NL80211_EXT_FEATURE_BEACON_RATE_LEGACY: Driver supports beacon rate + * configuration (AP/mesh), supporting a legacy (non HT/VHT) rate. + * @NL80211_EXT_FEATURE_BEACON_RATE_HT: Driver supports beacon rate + * configuration (AP/mesh) with HT rates. + * @NL80211_EXT_FEATURE_BEACON_RATE_VHT: Driver supports beacon rate + * configuration (AP/mesh) with VHT rates. + * @NL80211_EXT_FEATURE_FILS_STA: This driver supports Fast Initial Link Setup + * with user space SME (NL80211_CMD_AUTHENTICATE) in station mode. + * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA: This driver supports randomized TA + * in @NL80211_CMD_FRAME while not associated. + * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED: This driver supports + * randomized TA in @NL80211_CMD_FRAME while associated. + * @NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI: The driver supports sched_scan + * for reporting BSSs with better RSSI than the current connected BSS + * (%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI). + * @NL80211_EXT_FEATURE_CQM_RSSI_LIST: With this driver the + * %NL80211_ATTR_CQM_RSSI_THOLD attribute accepts a list of zero or more + * RSSI threshold values to monitor rather than exactly one threshold. + * @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD: Driver SME supports FILS shared key + * authentication with %NL80211_CMD_CONNECT. + * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK: Device wants to do 4-way + * handshake with PSK in station mode (PSK is passed as part of the connect + * and associate commands), doing it in the host might not be supported. + * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X: Device wants to do doing 4-way + * handshake with 802.1X in station mode (will pass EAP frames to the host + * and accept the set_pmk/del_pmk commands), doing it in the host might not + * be supported. + * @NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME: Driver is capable of overriding + * the max channel attribute in the FILS request params IE with the + * actual dwell time. + * @NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP: Driver accepts broadcast probe + * response + * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE: Driver supports sending + * the first probe request in each channel at rate of at least 5.5Mbps. + * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: Driver supports + * probe request tx deferral and suppression + * @NL80211_EXT_FEATURE_MFP_OPTIONAL: Driver supports the %NL80211_MFP_OPTIONAL + * value in %NL80211_ATTR_USE_MFP. + * @NL80211_EXT_FEATURE_LOW_SPAN_SCAN: Driver supports low span scan. + * @NL80211_EXT_FEATURE_LOW_POWER_SCAN: Driver supports low power scan. + * @NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN: Driver supports high accuracy scan. + * @NL80211_EXT_FEATURE_DFS_OFFLOAD: HW/driver will offload DFS actions. + * Device or driver will do all DFS-related actions by itself, + * informing user-space about CAC progress, radar detection event, + * channel change triggered by radar detection event. + * No need to start CAC from user-space, no need to react to + * "radar detected" event. + * @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211: Driver supports sending and + * receiving control port frames over nl80211 instead of the netdevice. + * @NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT: This driver/device supports + * (average) ACK signal strength reporting. + * @NL80211_EXT_FEATURE_TXQS: Driver supports FQ-CoDel-enabled intermediate + * TXQs. + * @NL80211_EXT_FEATURE_SCAN_RANDOM_SN: Driver/device supports randomizing the + * SN in probe request frames if requested by %NL80211_SCAN_FLAG_RANDOM_SN. + * @NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT: Driver/device can omit all data + * except for supported rates from the probe request content if requested + * by the %NL80211_SCAN_FLAG_MIN_PREQ_CONTENT flag. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -4562,6 +5236,33 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_SCAN_START_TIME, NL80211_EXT_FEATURE_BSS_PARENT_TSF, NL80211_EXT_FEATURE_SET_SCAN_DWELL, + NL80211_EXT_FEATURE_BEACON_RATE_LEGACY, + NL80211_EXT_FEATURE_BEACON_RATE_HT, + NL80211_EXT_FEATURE_BEACON_RATE_VHT, + NL80211_EXT_FEATURE_FILS_STA, + NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA, + NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED, + NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI, + NL80211_EXT_FEATURE_CQM_RSSI_LIST, + NL80211_EXT_FEATURE_FILS_SK_OFFLOAD, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X, + NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME, + NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION, + NL80211_EXT_FEATURE_MFP_OPTIONAL, + NL80211_EXT_FEATURE_LOW_SPAN_SCAN, + NL80211_EXT_FEATURE_LOW_POWER_SCAN, + NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN, + NL80211_EXT_FEATURE_DFS_OFFLOAD, + NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211, + NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT, + /* we renamed this - stay compatible */ + NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT = NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT, + NL80211_EXT_FEATURE_TXQS, + NL80211_EXT_FEATURE_SCAN_RANDOM_SN, + NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, @@ -4601,12 +5302,31 @@ enum nl80211_connect_failed_reason { }; /** + * enum nl80211_timeout_reason - timeout reasons + * + * @NL80211_TIMEOUT_UNSPECIFIED: Timeout reason unspecified. + * @NL80211_TIMEOUT_SCAN: Scan (AP discovery) timed out. + * @NL80211_TIMEOUT_AUTH: Authentication timed out. + * @NL80211_TIMEOUT_ASSOC: Association timed out. + */ +enum nl80211_timeout_reason { + NL80211_TIMEOUT_UNSPECIFIED, + NL80211_TIMEOUT_SCAN, + NL80211_TIMEOUT_AUTH, + NL80211_TIMEOUT_ASSOC, +}; + +/** * enum nl80211_scan_flags - scan request control flags * * Scan request control flags are used to control the handling * of NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_START_SCHED_SCAN * requests. * + * NL80211_SCAN_FLAG_LOW_SPAN, NL80211_SCAN_FLAG_LOW_POWER, and + * NL80211_SCAN_FLAG_HIGH_ACCURACY flags are exclusive of each other, i.e., only + * one of them can be used in the request. + * * @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority * @NL80211_SCAN_FLAG_FLUSH: flush cache before scanning * @NL80211_SCAN_FLAG_AP: force a scan even if the interface is configured @@ -4623,12 +5343,52 @@ enum nl80211_connect_failed_reason { * locally administered 1, multicast 0) is assumed. * This flag must not be requested when the feature isn't supported, check * the nl80211 feature flags for the device. + * @NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME: fill the dwell time in the FILS + * request parameters IE in the probe request + * @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses + * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at + * rate of at least 5.5M. In case non OCE AP is dicovered in the channel, + * only the first probe req in the channel will be sent in high rate. + * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request + * tx deferral (dot11FILSProbeDelay shall be set to 15ms) + * and suppression (if it has received a broadcast Probe Response frame, + * Beacon frame or FILS Discovery frame from an AP that the STA considers + * a suitable candidate for (re-)association - suitable in terms of + * SSID and/or RSSI. + * @NL80211_SCAN_FLAG_LOW_SPAN: Span corresponds to the total time taken to + * accomplish the scan. Thus, this flag intends the driver to perform the + * scan request with lesser span/duration. It is specific to the driver + * implementations on how this is accomplished. Scan accuracy may get + * impacted with this flag. + * @NL80211_SCAN_FLAG_LOW_POWER: This flag intends the scan attempts to consume + * optimal possible power. Drivers can resort to their specific means to + * optimize the power. Scan accuracy may get impacted with this flag. + * @NL80211_SCAN_FLAG_HIGH_ACCURACY: Accuracy here intends to the extent of scan + * results obtained. Thus HIGH_ACCURACY scan flag aims to get maximum + * possible scan results. This flag hints the driver to use the best + * possible scan configuration to improve the accuracy in scanning. + * Latency and power use may get impacted with this flag. + * @NL80211_SCAN_FLAG_RANDOM_SN: randomize the sequence number in probe + * request frames from this scan to avoid correlation/tracking being + * possible. + * @NL80211_SCAN_FLAG_MIN_PREQ_CONTENT: minimize probe request content to + * only have supported rates and no additional capabilities (unless + * added by userspace explicitly.) */ enum nl80211_scan_flags { - NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0, - NL80211_SCAN_FLAG_FLUSH = 1<<1, - NL80211_SCAN_FLAG_AP = 1<<2, - NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3, + NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0, + NL80211_SCAN_FLAG_FLUSH = 1<<1, + NL80211_SCAN_FLAG_AP = 1<<2, + NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3, + NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME = 1<<4, + NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP = 1<<5, + NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE = 1<<6, + NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION = 1<<7, + NL80211_SCAN_FLAG_LOW_SPAN = 1<<8, + NL80211_SCAN_FLAG_LOW_POWER = 1<<9, + NL80211_SCAN_FLAG_HIGH_ACCURACY = 1<<10, + NL80211_SCAN_FLAG_RANDOM_SN = 1<<11, + NL80211_SCAN_FLAG_MIN_PREQ_CONTENT = 1<<12, }; /** @@ -4682,12 +5442,20 @@ enum nl80211_smps_mode { * change to the channel status. * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is * over, channel becomes usable. + * @NL80211_RADAR_PRE_CAC_EXPIRED: Channel Availability Check done on this + * non-operating channel is expired and no longer valid. New CAC must + * be done on this channel before starting the operation. This is not + * applicable for ETSI dfs domain where pre-CAC is valid for ever. + * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started, + * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled. */ enum nl80211_radar_event { NL80211_RADAR_DETECTED, NL80211_RADAR_CAC_FINISHED, NL80211_RADAR_CAC_ABORTED, NL80211_RADAR_NOP_FINISHED, + NL80211_RADAR_PRE_CAC_EXPIRED, + NL80211_RADAR_CAC_STARTED, }; /** @@ -4814,8 +5582,9 @@ enum nl80211_sched_scan_plan { /** * struct nl80211_bss_select_rssi_adjust - RSSI adjustment parameters. * - * @band: band of BSS that must match for RSSI value adjustment. - * @delta: value used to adjust the RSSI value of matching BSS. + * @band: band of BSS that must match for RSSI value adjustment. The value + * of this field is according to &enum nl80211_band. + * @delta: value used to adjust the RSSI value of matching BSS in dB. */ struct nl80211_bss_select_rssi_adjust { __u8 band; @@ -4855,4 +5624,182 @@ enum nl80211_bss_select_attr { NL80211_BSS_SELECT_ATTR_MAX = __NL80211_BSS_SELECT_ATTR_AFTER_LAST - 1 }; +/** + * enum nl80211_nan_function_type - NAN function type + * + * Defines the function type of a NAN function + * + * @NL80211_NAN_FUNC_PUBLISH: function is publish + * @NL80211_NAN_FUNC_SUBSCRIBE: function is subscribe + * @NL80211_NAN_FUNC_FOLLOW_UP: function is follow-up + */ +enum nl80211_nan_function_type { + NL80211_NAN_FUNC_PUBLISH, + NL80211_NAN_FUNC_SUBSCRIBE, + NL80211_NAN_FUNC_FOLLOW_UP, + + /* keep last */ + __NL80211_NAN_FUNC_TYPE_AFTER_LAST, + NL80211_NAN_FUNC_MAX_TYPE = __NL80211_NAN_FUNC_TYPE_AFTER_LAST - 1, +}; + +/** + * enum nl80211_nan_publish_type - NAN publish tx type + * + * Defines how to send publish Service Discovery Frames + * + * @NL80211_NAN_SOLICITED_PUBLISH: publish function is solicited + * @NL80211_NAN_UNSOLICITED_PUBLISH: publish function is unsolicited + */ +enum nl80211_nan_publish_type { + NL80211_NAN_SOLICITED_PUBLISH = 1 << 0, + NL80211_NAN_UNSOLICITED_PUBLISH = 1 << 1, +}; + +/** + * enum nl80211_nan_func_term_reason - NAN functions termination reason + * + * Defines termination reasons of a NAN function + * + * @NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST: requested by user + * @NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED: timeout + * @NL80211_NAN_FUNC_TERM_REASON_ERROR: errored + */ +enum nl80211_nan_func_term_reason { + NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST, + NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED, + NL80211_NAN_FUNC_TERM_REASON_ERROR, +}; + +#define NL80211_NAN_FUNC_SERVICE_ID_LEN 6 +#define NL80211_NAN_FUNC_SERVICE_SPEC_INFO_MAX_LEN 0xff +#define NL80211_NAN_FUNC_SRF_MAX_LEN 0xff + +/** + * enum nl80211_nan_func_attributes - NAN function attributes + * @__NL80211_NAN_FUNC_INVALID: invalid + * @NL80211_NAN_FUNC_TYPE: &enum nl80211_nan_function_type (u8). + * @NL80211_NAN_FUNC_SERVICE_ID: 6 bytes of the service ID hash as + * specified in NAN spec. This is a binary attribute. + * @NL80211_NAN_FUNC_PUBLISH_TYPE: relevant if the function's type is + * publish. Defines the transmission type for the publish Service Discovery + * Frame, see &enum nl80211_nan_publish_type. Its type is u8. + * @NL80211_NAN_FUNC_PUBLISH_BCAST: relevant if the function is a solicited + * publish. Should the solicited publish Service Discovery Frame be sent to + * the NAN Broadcast address. This is a flag. + * @NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE: relevant if the function's type is + * subscribe. Is the subscribe active. This is a flag. + * @NL80211_NAN_FUNC_FOLLOW_UP_ID: relevant if the function's type is follow up. + * The instance ID for the follow up Service Discovery Frame. This is u8. + * @NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID: relevant if the function's type + * is follow up. This is a u8. + * The requestor instance ID for the follow up Service Discovery Frame. + * @NL80211_NAN_FUNC_FOLLOW_UP_DEST: the MAC address of the recipient of the + * follow up Service Discovery Frame. This is a binary attribute. + * @NL80211_NAN_FUNC_CLOSE_RANGE: is this function limited for devices in a + * close range. The range itself (RSSI) is defined by the device. + * This is a flag. + * @NL80211_NAN_FUNC_TTL: strictly positive number of DWs this function should + * stay active. If not present infinite TTL is assumed. This is a u32. + * @NL80211_NAN_FUNC_SERVICE_INFO: array of bytes describing the service + * specific info. This is a binary attribute. + * @NL80211_NAN_FUNC_SRF: Service Receive Filter. This is a nested attribute. + * See &enum nl80211_nan_srf_attributes. + * @NL80211_NAN_FUNC_RX_MATCH_FILTER: Receive Matching filter. This is a nested + * attribute. It is a list of binary values. + * @NL80211_NAN_FUNC_TX_MATCH_FILTER: Transmit Matching filter. This is a + * nested attribute. It is a list of binary values. + * @NL80211_NAN_FUNC_INSTANCE_ID: The instance ID of the function. + * Its type is u8 and it cannot be 0. + * @NL80211_NAN_FUNC_TERM_REASON: NAN function termination reason. + * See &enum nl80211_nan_func_term_reason. + * + * @NUM_NL80211_NAN_FUNC_ATTR: internal + * @NL80211_NAN_FUNC_ATTR_MAX: highest NAN function attribute + */ +enum nl80211_nan_func_attributes { + __NL80211_NAN_FUNC_INVALID, + NL80211_NAN_FUNC_TYPE, + NL80211_NAN_FUNC_SERVICE_ID, + NL80211_NAN_FUNC_PUBLISH_TYPE, + NL80211_NAN_FUNC_PUBLISH_BCAST, + NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE, + NL80211_NAN_FUNC_FOLLOW_UP_ID, + NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID, + NL80211_NAN_FUNC_FOLLOW_UP_DEST, + NL80211_NAN_FUNC_CLOSE_RANGE, + NL80211_NAN_FUNC_TTL, + NL80211_NAN_FUNC_SERVICE_INFO, + NL80211_NAN_FUNC_SRF, + NL80211_NAN_FUNC_RX_MATCH_FILTER, + NL80211_NAN_FUNC_TX_MATCH_FILTER, + NL80211_NAN_FUNC_INSTANCE_ID, + NL80211_NAN_FUNC_TERM_REASON, + + /* keep last */ + NUM_NL80211_NAN_FUNC_ATTR, + NL80211_NAN_FUNC_ATTR_MAX = NUM_NL80211_NAN_FUNC_ATTR - 1 +}; + +/** + * enum nl80211_nan_srf_attributes - NAN Service Response filter attributes + * @__NL80211_NAN_SRF_INVALID: invalid + * @NL80211_NAN_SRF_INCLUDE: present if the include bit of the SRF set. + * This is a flag. + * @NL80211_NAN_SRF_BF: Bloom Filter. Present if and only if + * %NL80211_NAN_SRF_MAC_ADDRS isn't present. This attribute is binary. + * @NL80211_NAN_SRF_BF_IDX: index of the Bloom Filter. Mandatory if + * %NL80211_NAN_SRF_BF is present. This is a u8. + * @NL80211_NAN_SRF_MAC_ADDRS: list of MAC addresses for the SRF. Present if + * and only if %NL80211_NAN_SRF_BF isn't present. This is a nested + * attribute. Each nested attribute is a MAC address. + * @NUM_NL80211_NAN_SRF_ATTR: internal + * @NL80211_NAN_SRF_ATTR_MAX: highest NAN SRF attribute + */ +enum nl80211_nan_srf_attributes { + __NL80211_NAN_SRF_INVALID, + NL80211_NAN_SRF_INCLUDE, + NL80211_NAN_SRF_BF, + NL80211_NAN_SRF_BF_IDX, + NL80211_NAN_SRF_MAC_ADDRS, + + /* keep last */ + NUM_NL80211_NAN_SRF_ATTR, + NL80211_NAN_SRF_ATTR_MAX = NUM_NL80211_NAN_SRF_ATTR - 1, +}; + +/** + * enum nl80211_nan_match_attributes - NAN match attributes + * @__NL80211_NAN_MATCH_INVALID: invalid + * @NL80211_NAN_MATCH_FUNC_LOCAL: the local function that had the + * match. This is a nested attribute. + * See &enum nl80211_nan_func_attributes. + * @NL80211_NAN_MATCH_FUNC_PEER: the peer function + * that caused the match. This is a nested attribute. + * See &enum nl80211_nan_func_attributes. + * + * @NUM_NL80211_NAN_MATCH_ATTR: internal + * @NL80211_NAN_MATCH_ATTR_MAX: highest NAN match attribute + */ +enum nl80211_nan_match_attributes { + __NL80211_NAN_MATCH_INVALID, + NL80211_NAN_MATCH_FUNC_LOCAL, + NL80211_NAN_MATCH_FUNC_PEER, + + /* keep last */ + NUM_NL80211_NAN_MATCH_ATTR, + NL80211_NAN_MATCH_ATTR_MAX = NUM_NL80211_NAN_MATCH_ATTR - 1 +}; + +/** + * nl80211_external_auth_action - Action to perform with external + * authentication request. Used by NL80211_ATTR_EXTERNAL_AUTH_ACTION. + * @NL80211_EXTERNAL_AUTH_START: Start the authentication. + * @NL80211_EXTERNAL_AUTH_ABORT: Abort the ongoing authentication. + */ +enum nl80211_external_auth_action { + NL80211_EXTERNAL_AUTH_START, + NL80211_EXTERNAL_AUTH_ABORT, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c index 621746821538..bfe8811e33f2 100644 --- a/src/eap_common/eap_eke_common.c +++ b/src/eap_common/eap_eke_common.c @@ -161,7 +161,6 @@ int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub) int generator; u8 gen; const struct dh_group *dh; - size_t pub_len, i; generator = eap_eke_dh_generator(group); dh = eap_eke_dh_group(group); @@ -169,33 +168,11 @@ int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub) return -1; gen = generator; - /* x = random number 2 .. p-1 */ - if (random_get_bytes(ret_priv, dh->prime_len)) - return -1; - if (os_memcmp(ret_priv, dh->prime, dh->prime_len) > 0) { - /* Make sure private value is smaller than prime */ - ret_priv[0] = 0; - } - for (i = 0; i < dh->prime_len - 1; i++) { - if (ret_priv[i]) - break; - } - if (i == dh->prime_len - 1 && (ret_priv[i] == 0 || ret_priv[i] == 1)) + if (crypto_dh_init(gen, dh->prime, dh->prime_len, ret_priv, + ret_pub) < 0) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: DH private value", ret_priv, dh->prime_len); - - /* y = g ^ x (mod p) */ - pub_len = dh->prime_len; - if (crypto_mod_exp(&gen, 1, ret_priv, dh->prime_len, - dh->prime, dh->prime_len, ret_pub, &pub_len) < 0) - return -1; - if (pub_len < dh->prime_len) { - size_t pad = dh->prime_len - pub_len; - os_memmove(ret_pub + pad, ret_pub, pub_len); - os_memset(ret_pub, 0, pad); - } - wpa_hexdump(MSG_DEBUG, "EAP-EKE: DH public value", ret_pub, dh->prime_len); @@ -421,8 +398,9 @@ int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key, /* SharedSecret = prf(0+, g ^ (x_s * x_p) (mod p)) */ len = dh->prime_len; - if (crypto_mod_exp(peer_pub, dh->prime_len, dhpriv, dh->prime_len, - dh->prime, dh->prime_len, modexp, &len) < 0) + if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len, + dhpriv, dh->prime_len, peer_pub, + dh->prime_len, modexp, &len) < 0) return -1; if (len < dh->prime_len) { size_t pad = dh->prime_len - len; diff --git a/src/eap_common/eap_fast_common.c b/src/eap_common/eap_fast_common.c index 9ef671c41c7d..57990d254a6a 100644 --- a/src/eap_common/eap_fast_common.c +++ b/src/eap_common/eap_fast_common.c @@ -79,7 +79,7 @@ void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random, /* * RFC 4851, Section 5.1: - * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash", + * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash", * server_random + client_random, 48) */ os_memcpy(seed, server_random, TLS_RANDOM_LEN); diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c index 67f8f7098c4b..88c6595887ab 100644 --- a/src/eap_common/eap_pwd_common.c +++ b/src/eap_common/eap_pwd_common.c @@ -81,6 +81,27 @@ static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label, } +EAP_PWD_group * get_eap_pwd_group(u16 num) +{ + EAP_PWD_group *grp; + + grp = os_zalloc(sizeof(EAP_PWD_group)); + if (!grp) + return NULL; + grp->group = crypto_ec_init(num); + if (!grp->group) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC group"); + os_free(grp); + return NULL; + } + + grp->group_num = num; + wpa_printf(MSG_INFO, "EAP-pwd: provisioned group %d", num); + + return grp; +} + + /* * compute a "random" secret point on an elliptic curve based * on the password and identities. @@ -91,105 +112,71 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, const u8 *id_peer, size_t id_peer_len, const u8 *token) { - BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; + struct crypto_bignum *qr = NULL, *qnr = NULL, *one = NULL; + struct crypto_bignum *tmp1 = NULL, *tmp2 = NULL, *pm1 = NULL; struct crypto_hash *hash; unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr; - int nid, is_odd, ret = 0; + int is_odd, ret = 0, check, found = 0; size_t primebytelen, primebitlen; + struct crypto_bignum *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; + const struct crypto_bignum *prime; - switch (num) { /* from IANA registry for IKE D-H groups */ - case 19: - nid = NID_X9_62_prime256v1; - break; - case 20: - nid = NID_secp384r1; - break; - case 21: - nid = NID_secp521r1; - break; -#ifndef OPENSSL_IS_BORINGSSL - case 25: - nid = NID_X9_62_prime192v1; - break; -#endif /* OPENSSL_IS_BORINGSSL */ - case 26: - nid = NID_secp224r1; - break; -#ifdef NID_brainpoolP224r1 - case 27: - nid = NID_brainpoolP224r1; - break; -#endif /* NID_brainpoolP224r1 */ -#ifdef NID_brainpoolP256r1 - case 28: - nid = NID_brainpoolP256r1; - break; -#endif /* NID_brainpoolP256r1 */ -#ifdef NID_brainpoolP384r1 - case 29: - nid = NID_brainpoolP384r1; - break; -#endif /* NID_brainpoolP384r1 */ -#ifdef NID_brainpoolP512r1 - case 30: - nid = NID_brainpoolP512r1; - break; -#endif /* NID_brainpoolP512r1 */ - default: - wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num); + if (grp->pwe) return -1; - } - - grp->pwe = NULL; - grp->order = NULL; - grp->prime = NULL; - - if ((grp->group = EC_GROUP_new_by_curve_name(nid)) == NULL) { - wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP"); - goto fail; - } - if (((rnd = BN_new()) == NULL) || - ((cofactor = BN_new()) == NULL) || - ((grp->pwe = EC_POINT_new(grp->group)) == NULL) || - ((grp->order = BN_new()) == NULL) || - ((grp->prime = BN_new()) == NULL) || - ((x_candidate = BN_new()) == NULL)) { + prime = crypto_ec_get_prime(grp->group); + cofactor = crypto_bignum_init(); + grp->pwe = crypto_ec_point_init(grp->group); + tmp1 = crypto_bignum_init(); + pm1 = crypto_bignum_init(); + one = crypto_bignum_init_set((const u8 *) "\x01", 1); + if (!cofactor || !grp->pwe || !tmp1 || !pm1 || !one) { wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums"); goto fail; } - if (!EC_GROUP_get_curve_GFp(grp->group, grp->prime, NULL, NULL, NULL)) - { - wpa_printf(MSG_INFO, "EAP-pwd: unable to get prime for GFp " - "curve"); - goto fail; - } - if (!EC_GROUP_get_order(grp->group, grp->order, NULL)) { - wpa_printf(MSG_INFO, "EAP-pwd: unable to get order for curve"); - goto fail; - } - if (!EC_GROUP_get_cofactor(grp->group, cofactor, NULL)) { + if (crypto_ec_cofactor(grp->group, cofactor) < 0) { wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for " "curve"); goto fail; } - primebitlen = BN_num_bits(grp->prime); - primebytelen = BN_num_bytes(grp->prime); + primebitlen = crypto_ec_prime_len_bits(grp->group); + primebytelen = crypto_ec_prime_len(grp->group); if ((prfbuf = os_malloc(primebytelen)) == NULL) { wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf " "buffer"); goto fail; } - os_memset(prfbuf, 0, primebytelen); - ctr = 0; - while (1) { - if (ctr > 30) { - wpa_printf(MSG_INFO, "EAP-pwd: unable to find random " - "point on curve for group %d, something's " - "fishy", num); + if (crypto_bignum_sub(prime, one, pm1) < 0) + goto fail; + + /* get a random quadratic residue and nonresidue */ + while (!qr || !qnr) { + int res; + + if (crypto_bignum_rand(tmp1, prime) < 0) goto fail; + res = crypto_bignum_legendre(tmp1, prime); + if (!qr && res == 1) { + qr = tmp1; + tmp1 = crypto_bignum_init(); + } else if (!qnr && res == -1) { + qnr = tmp1; + tmp1 = crypto_bignum_init(); } + if (!tmp1) + goto fail; + } + + os_memset(prfbuf, 0, primebytelen); + ctr = 0; + + /* + * Run through the hunting-and-pecking loop 40 times to mask the time + * necessary to find PWE. The odds of PWE not being found in 40 loops is + * roughly 1 in 1 trillion. + */ + while (ctr < 40) { ctr++; /* @@ -207,15 +194,25 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, eap_pwd_h_update(hash, &ctr, sizeof(ctr)); eap_pwd_h_final(hash, pwe_digest); - BN_bin2bn(pwe_digest, SHA256_MAC_LEN, rnd); - + crypto_bignum_deinit(rnd, 1); + rnd = crypto_bignum_init_set(pwe_digest, SHA256_MAC_LEN); + if (!rnd) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to create rnd"); + goto fail; + } if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN, (u8 *) "EAP-pwd Hunting And Pecking", os_strlen("EAP-pwd Hunting And Pecking"), prfbuf, primebitlen) < 0) goto fail; - BN_bin2bn(prfbuf, primebytelen, x_candidate); + crypto_bignum_deinit(x_candidate, 1); + x_candidate = crypto_bignum_init_set(prfbuf, primebytelen); + if (!x_candidate) { + wpa_printf(MSG_INFO, + "EAP-pwd: unable to create x_candidate"); + goto fail; + } /* * eap_pwd_kdf() returns a string of bits 0..primebitlen but @@ -224,97 +221,157 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, * then excessive bits-- those _after_ primebitlen-- so now * we have to shift right the amount we masked off. */ - if (primebitlen % 8) - BN_rshift(x_candidate, x_candidate, - (8 - (primebitlen % 8))); + if ((primebitlen % 8) && + crypto_bignum_rshift(x_candidate, + (8 - (primebitlen % 8)), + x_candidate) < 0) + goto fail; - if (BN_ucmp(x_candidate, grp->prime) >= 0) + if (crypto_bignum_cmp(x_candidate, prime) >= 0) continue; wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate", prfbuf, primebytelen); /* - * need to unambiguously identify the solution, if there is - * one... + * compute y^2 using the equation of the curve + * + * y^2 = x^3 + ax + b */ - if (BN_is_odd(rnd)) - is_odd = 1; - else - is_odd = 0; + tmp2 = crypto_ec_point_compute_y_sqr(grp->group, x_candidate); + if (!tmp2) + goto fail; /* - * solve the quadratic equation, if it's not solvable then we - * don't have a point + * mask tmp2 so doing legendre won't leak timing info + * + * tmp1 is a random number between 1 and p-1 */ - if (!EC_POINT_set_compressed_coordinates_GFp(grp->group, - grp->pwe, - x_candidate, - is_odd, NULL)) - continue; + if (crypto_bignum_rand(tmp1, pm1) < 0 || + crypto_bignum_mulmod(tmp2, tmp1, prime, tmp2) < 0 || + crypto_bignum_mulmod(tmp2, tmp1, prime, tmp2) < 0) + goto fail; + /* - * If there's a solution to the equation then the point must be - * on the curve so why check again explicitly? OpenSSL code - * says this is required by X9.62. We're not X9.62 but it can't - * hurt just to be sure. + * Now tmp2 (y^2) is masked, all values between 1 and p-1 + * are equally probable. Multiplying by r^2 does not change + * whether or not tmp2 is a quadratic residue, just masks it. + * + * Flip a coin, multiply by the random quadratic residue or the + * random quadratic nonresidue and record heads or tails. */ - if (!EC_POINT_is_on_curve(grp->group, grp->pwe, NULL)) { - wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve"); - continue; + if (crypto_bignum_is_odd(tmp1)) { + crypto_bignum_mulmod(tmp2, qr, prime, tmp2); + check = 1; + } else { + crypto_bignum_mulmod(tmp2, qnr, prime, tmp2); + check = -1; } - if (BN_cmp(cofactor, BN_value_one())) { - /* make sure the point is not in a small sub-group */ - if (!EC_POINT_mul(grp->group, grp->pwe, NULL, grp->pwe, - cofactor, NULL)) { - wpa_printf(MSG_INFO, "EAP-pwd: cannot " - "multiply generator by order"); + /* + * Now it's safe to do legendre, if check is 1 then it's + * a straightforward test (multiplying by qr does not + * change result), if check is -1 then it's the opposite test + * (multiplying a qr by qnr would make a qnr). + */ + if (crypto_bignum_legendre(tmp2, prime) == check) { + if (found == 1) + continue; + + /* need to unambiguously identify the solution */ + is_odd = crypto_bignum_is_odd(rnd); + + /* + * We know x_candidate is a quadratic residue so set + * it here. + */ + if (crypto_ec_point_solve_y_coord(grp->group, grp->pwe, + x_candidate, + is_odd) != 0) { + wpa_printf(MSG_INFO, + "EAP-pwd: Could not solve for y"); continue; } - if (EC_POINT_is_at_infinity(grp->group, grp->pwe)) { - wpa_printf(MSG_INFO, "EAP-pwd: point is at " - "infinity"); + + /* + * If there's a solution to the equation then the point + * must be on the curve so why check again explicitly? + * OpenSSL code says this is required by X9.62. We're + * not X9.62 but it can't hurt just to be sure. + */ + if (!crypto_ec_point_is_on_curve(grp->group, + grp->pwe)) { + wpa_printf(MSG_INFO, + "EAP-pwd: point is not on curve"); continue; } + + if (!crypto_bignum_is_one(cofactor)) { + /* make sure the point is not in a small + * sub-group */ + if (crypto_ec_point_mul(grp->group, grp->pwe, + cofactor, + grp->pwe) != 0) { + wpa_printf(MSG_INFO, + "EAP-pwd: cannot multiply generator by order"); + continue; + } + if (crypto_ec_point_is_at_infinity(grp->group, + grp->pwe)) { + wpa_printf(MSG_INFO, + "EAP-pwd: point is at infinity"); + continue; + } + } + wpa_printf(MSG_DEBUG, + "EAP-pwd: found a PWE in %d tries", ctr); + found = 1; } - /* if we got here then we have a new generator. */ - break; } - wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr); - grp->group_num = num; + if (found == 0) { + wpa_printf(MSG_INFO, + "EAP-pwd: unable to find random point on curve for group %d, something's fishy", + num); + goto fail; + } if (0) { fail: - EC_GROUP_free(grp->group); - grp->group = NULL; - EC_POINT_clear_free(grp->pwe); + crypto_ec_point_deinit(grp->pwe, 1); grp->pwe = NULL; - BN_clear_free(grp->order); - grp->order = NULL; - BN_clear_free(grp->prime); - grp->prime = NULL; ret = 1; } /* cleanliness and order.... */ - BN_clear_free(cofactor); - BN_clear_free(x_candidate); - BN_clear_free(rnd); + crypto_bignum_deinit(cofactor, 1); + crypto_bignum_deinit(x_candidate, 1); + crypto_bignum_deinit(rnd, 1); + crypto_bignum_deinit(pm1, 0); + crypto_bignum_deinit(tmp1, 1); + crypto_bignum_deinit(tmp2, 1); + crypto_bignum_deinit(qr, 1); + crypto_bignum_deinit(qnr, 1); + crypto_bignum_deinit(one, 0); os_free(prfbuf); return ret; } -int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k, - const BIGNUM *peer_scalar, const BIGNUM *server_scalar, +int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k, + const struct crypto_bignum *peer_scalar, + const struct crypto_bignum *server_scalar, const u8 *confirm_peer, const u8 *confirm_server, const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id) { struct crypto_hash *hash; u8 mk[SHA256_MAC_LEN], *cruft; u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN]; - int offset; + size_t prime_len, order_len; + + prime_len = crypto_ec_prime_len(grp->group); + order_len = crypto_ec_order_len(grp->group); - if ((cruft = os_malloc(BN_num_bytes(grp->prime))) == NULL) + cruft = os_malloc(prime_len); + if (!cruft) return -1; /* @@ -328,14 +385,10 @@ int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k, return -1; } eap_pwd_h_update(hash, (const u8 *) ciphersuite, sizeof(u32)); - offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar); - os_memset(cruft, 0, BN_num_bytes(grp->prime)); - BN_bn2bin(peer_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order)); - offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar); - os_memset(cruft, 0, BN_num_bytes(grp->prime)); - BN_bn2bin(server_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order)); + crypto_bignum_to_bin(peer_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); + crypto_bignum_to_bin(server_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); eap_pwd_h_final(hash, &session_id[1]); /* then compute MK = H(k | confirm-peer | confirm-server) */ @@ -344,10 +397,8 @@ int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k, os_free(cruft); return -1; } - offset = BN_num_bytes(grp->prime) - BN_num_bytes(k); - os_memset(cruft, 0, BN_num_bytes(grp->prime)); - BN_bn2bin(k, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->prime)); + crypto_bignum_to_bin(k, cruft, prime_len, prime_len); + eap_pwd_h_update(hash, cruft, prime_len); os_free(cruft); eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN); eap_pwd_h_update(hash, confirm_server, SHA256_MAC_LEN); diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h index a0d717edfe7a..6b07cf8f797c 100644 --- a/src/eap_common/eap_pwd_common.h +++ b/src/eap_common/eap_pwd_common.h @@ -9,20 +9,14 @@ #ifndef EAP_PWD_COMMON_H #define EAP_PWD_COMMON_H -#include <openssl/bn.h> -#include <openssl/ec.h> -#include <openssl/evp.h> - /* * definition of a finite cyclic group * TODO: support one based on a prime field */ typedef struct group_definition_ { u16 group_num; - EC_GROUP *group; - EC_POINT *pwe; - BIGNUM *order; - BIGNUM *prime; + struct crypto_ec *group; + struct crypto_ec_point *pwe; } EAP_PWD_group; /* @@ -52,17 +46,22 @@ struct eap_pwd_id { u8 prep; #define EAP_PWD_PREP_NONE 0 #define EAP_PWD_PREP_MS 1 +#define EAP_PWD_PREP_SSHA1 3 +#define EAP_PWD_PREP_SSHA256 4 +#define EAP_PWD_PREP_SSHA512 5 u8 identity[0]; /* length inferred from payload */ } STRUCT_PACKED; /* common routines */ +EAP_PWD_group * get_eap_pwd_group(u16 num); int compute_password_element(EAP_PWD_group *grp, u16 num, const u8 *password, size_t password_len, const u8 *id_server, size_t id_server_len, const u8 *id_peer, size_t id_peer_len, const u8 *token); -int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k, - const BIGNUM *peer_scalar, const BIGNUM *server_scalar, +int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k, + const struct crypto_bignum *peer_scalar, + const struct crypto_bignum *server_scalar, const u8 *confirm_peer, const u8 *confirm_server, const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id); struct crypto_hash * eap_pwd_h_init(void); diff --git a/src/eap_common/eap_sim_common.c b/src/eap_common/eap_sim_common.c index 2adc3b376a8e..6290c35f1a6b 100644 --- a/src/eap_common/eap_sim_common.c +++ b/src/eap_common/eap_sim_common.c @@ -175,7 +175,7 @@ int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req, mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN) return -1; - tmp = os_malloc(wpabuf_len(req)); + tmp = os_memdup(wpabuf_head(req), wpabuf_len(req)); if (tmp == NULL) return -1; @@ -185,7 +185,6 @@ int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req, len[1] = extra_len; /* HMAC-SHA1-128 */ - os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req)); os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - msg", tmp, wpabuf_len(req)); @@ -370,7 +369,7 @@ int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req, mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN) return -1; - tmp = os_malloc(wpabuf_len(req)); + tmp = os_memdup(wpabuf_head(req), wpabuf_len(req)); if (tmp == NULL) return -1; @@ -380,7 +379,6 @@ int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req, len[1] = extra_len; /* HMAC-SHA-256-128 */ - os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req)); os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - msg", tmp, wpabuf_len(req)); @@ -943,10 +941,9 @@ u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, return NULL; } - decrypted = os_malloc(encr_data_len); + decrypted = os_memdup(encr_data, encr_data_len); if (decrypted == NULL) return NULL; - os_memcpy(decrypted, encr_data, encr_data_len); if (aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len)) { os_free(decrypted); diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index 9110ca5b9cfd..974c475ff2d4 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -95,6 +95,14 @@ static void eap_notify_status(struct eap_sm *sm, const char *status, } +static void eap_report_error(struct eap_sm *sm, int error_code) +{ + wpa_printf(MSG_DEBUG, "EAP: Error notification: %d", error_code); + if (sm->eapol_cb->notify_eap_error) + sm->eapol_cb->notify_eap_error(sm->eapol_ctx, error_code); +} + + static void eap_sm_free_key(struct eap_sm *sm) { if (sm->eapKeyData) { @@ -121,15 +129,17 @@ static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt) /** - * eap_allowed_method - Check whether EAP method is allowed + * eap_config_allowed_method - Check whether EAP method is allowed * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @config: EAP configuration * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types * @method: EAP type * Returns: 1 = allowed EAP method, 0 = not allowed */ -int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method) +static int eap_config_allowed_method(struct eap_sm *sm, + struct eap_peer_config *config, + int vendor, u32 method) { - struct eap_peer_config *config = eap_get_config(sm); int i; struct eap_method_type *m; @@ -146,6 +156,57 @@ int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method) } +/** + * eap_allowed_method - Check whether EAP method is allowed + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types + * @method: EAP type + * Returns: 1 = allowed EAP method, 0 = not allowed + */ +int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method) +{ + return eap_config_allowed_method(sm, eap_get_config(sm), vendor, + method); +} + + +#if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY) +static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi, + size_t max_len, size_t *imsi_len, + int mnc_len) +{ + char *pos, mnc[4]; + + if (*imsi_len + 36 > max_len) { + wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer"); + return -1; + } + + if (mnc_len != 2 && mnc_len != 3) + mnc_len = 3; + + if (mnc_len == 2) { + mnc[0] = '0'; + mnc[1] = imsi[3]; + mnc[2] = imsi[4]; + } else if (mnc_len == 3) { + mnc[0] = imsi[3]; + mnc[1] = imsi[4]; + mnc[2] = imsi[5]; + } + mnc[3] = '\0'; + + pos = imsi + *imsi_len; + pos += os_snprintf(pos, imsi + max_len - pos, + "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org", + mnc, imsi[0], imsi[1], imsi[2]); + *imsi_len = pos - imsi; + + return 0; +} +#endif /* PCSC_FUNCS || CONFIG_EAP_PROXY */ + + /* * This state initializes state machine variables when the machine is * activated (portEnabled = TRUE). This is also used when re-starting @@ -371,9 +432,8 @@ nak: #ifdef CONFIG_ERP -static char * eap_home_realm(struct eap_sm *sm) +static char * eap_get_realm(struct eap_sm *sm, struct eap_peer_config *config) { - struct eap_peer_config *config = eap_get_config(sm); char *realm; size_t i, realm_len; @@ -413,7 +473,51 @@ static char * eap_home_realm(struct eap_sm *sm) } } - return os_strdup(""); +#ifdef CONFIG_EAP_PROXY + /* When identity is not provided in the config, build the realm from + * IMSI for eap_proxy based methods. + */ + if (!config->identity && !config->anonymous_identity && + sm->eapol_cb->get_imsi && + (eap_config_allowed_method(sm, config, EAP_VENDOR_IETF, + EAP_TYPE_SIM) || + eap_config_allowed_method(sm, config, EAP_VENDOR_IETF, + EAP_TYPE_AKA) || + eap_config_allowed_method(sm, config, EAP_VENDOR_IETF, + EAP_TYPE_AKA_PRIME))) { + char imsi[100]; + size_t imsi_len; + int mnc_len, pos; + + wpa_printf(MSG_DEBUG, "EAP: Build realm from IMSI (eap_proxy)"); + mnc_len = sm->eapol_cb->get_imsi(sm->eapol_ctx, config->sim_num, + imsi, &imsi_len); + if (mnc_len < 0) + return NULL; + + pos = imsi_len + 1; /* points to the beginning of the realm */ + if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len, + mnc_len) < 0) { + wpa_printf(MSG_WARNING, "Could not append realm"); + return NULL; + } + + realm = os_strdup(&imsi[pos]); + if (!realm) + return NULL; + + wpa_printf(MSG_DEBUG, "EAP: Generated realm '%s'", realm); + return realm; + } +#endif /* CONFIG_EAP_PROXY */ + + return NULL; +} + + +static char * eap_home_realm(struct eap_sm *sm) +{ + return eap_get_realm(sm, eap_get_config(sm)); } @@ -469,6 +573,89 @@ static void eap_erp_remove_keys_realm(struct eap_sm *sm, const char *realm) } } + +int eap_peer_update_erp_next_seq_num(struct eap_sm *sm, u16 next_seq_num) +{ + struct eap_erp_key *erp; + char *home_realm; + + home_realm = eap_home_realm(sm); + if (!home_realm || os_strlen(home_realm) == 0) { + os_free(home_realm); + return -1; + } + + erp = eap_erp_get_key(sm, home_realm); + if (!erp) { + wpa_printf(MSG_DEBUG, + "EAP: Failed to find ERP key for realm: %s", + home_realm); + os_free(home_realm); + return -1; + } + + if ((u32) next_seq_num < erp->next_seq) { + /* Sequence number has wrapped around, clear this ERP + * info and do a full auth next time. + */ + eap_peer_erp_free_key(erp); + } else { + erp->next_seq = (u32) next_seq_num; + } + + os_free(home_realm); + return 0; +} + + +int eap_peer_get_erp_info(struct eap_sm *sm, struct eap_peer_config *config, + const u8 **username, size_t *username_len, + const u8 **realm, size_t *realm_len, + u16 *erp_next_seq_num, const u8 **rrk, + size_t *rrk_len) +{ + struct eap_erp_key *erp; + char *home_realm; + char *pos; + + if (config) + home_realm = eap_get_realm(sm, config); + else + home_realm = eap_home_realm(sm); + if (!home_realm || os_strlen(home_realm) == 0) { + os_free(home_realm); + return -1; + } + + erp = eap_erp_get_key(sm, home_realm); + os_free(home_realm); + if (!erp) + return -1; + + if (erp->next_seq >= 65536) + return -1; /* SEQ has range of 0..65535 */ + + pos = os_strchr(erp->keyname_nai, '@'); + if (!pos) + return -1; /* this cannot really happen */ + *username_len = pos - erp->keyname_nai; + *username = (u8 *) erp->keyname_nai; + + pos++; + *realm_len = os_strlen(pos); + *realm = (u8 *) pos; + + *erp_next_seq_num = (u16) erp->next_seq; + + *rrk_len = erp->rRK_len; + *rrk = erp->rRK; + + if (*username_len == 0 || *realm_len == 0 || *rrk_len == 0) + return -1; + + return 0; +} + #endif /* CONFIG_ERP */ @@ -483,13 +670,20 @@ void eap_peer_erp_free_keys(struct eap_sm *sm) } -static void eap_peer_erp_init(struct eap_sm *sm) +/* Note: If ext_session and/or ext_emsk are passed to this function, they are + * expected to point to allocated memory and those allocations will be freed + * unconditionally. */ +void eap_peer_erp_init(struct eap_sm *sm, u8 *ext_session_id, + size_t ext_session_id_len, u8 *ext_emsk, + size_t ext_emsk_len) { #ifdef CONFIG_ERP u8 *emsk = NULL; size_t emsk_len = 0; + u8 *session_id = NULL; + size_t session_id_len = 0; u8 EMSKname[EAP_EMSK_NAME_LEN]; - u8 len[2]; + u8 len[2], ctx[3]; char *realm; size_t realm_len, nai_buf_len; struct eap_erp_key *erp = NULL; @@ -497,7 +691,7 @@ static void eap_peer_erp_init(struct eap_sm *sm) realm = eap_home_realm(sm); if (!realm) - return; + goto fail; realm_len = os_strlen(realm); wpa_printf(MSG_DEBUG, "EAP: Realm for ERP keyName-NAI: %s", realm); eap_erp_remove_keys_realm(sm, realm); @@ -517,7 +711,13 @@ static void eap_peer_erp_init(struct eap_sm *sm) if (erp == NULL) goto fail; - emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len); + if (ext_emsk) { + emsk = ext_emsk; + emsk_len = ext_emsk_len; + } else { + emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len); + } + if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) { wpa_printf(MSG_DEBUG, "EAP: No suitable EMSK available for ERP"); @@ -526,10 +726,23 @@ static void eap_peer_erp_init(struct eap_sm *sm) wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len); - WPA_PUT_BE16(len, 8); - if (hmac_sha256_kdf(sm->eapSessionId, sm->eapSessionIdLen, "EMSK", - len, sizeof(len), - EMSKname, EAP_EMSK_NAME_LEN) < 0) { + if (ext_session_id) { + session_id = ext_session_id; + session_id_len = ext_session_id_len; + } else { + session_id = sm->eapSessionId; + session_id_len = sm->eapSessionIdLen; + } + + if (!session_id || session_id_len == 0) { + wpa_printf(MSG_DEBUG, + "EAP: No suitable session id available for ERP"); + goto fail; + } + + WPA_PUT_BE16(len, EAP_EMSK_NAME_LEN); + if (hmac_sha256_kdf(session_id, session_id_len, "EMSK", len, + sizeof(len), EMSKname, EAP_EMSK_NAME_LEN) < 0) { wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname"); goto fail; } @@ -550,9 +763,11 @@ static void eap_peer_erp_init(struct eap_sm *sm) erp->rRK_len = emsk_len; wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len); + ctx[0] = EAP_ERP_CS_HMAC_SHA256_128; + WPA_PUT_BE16(&ctx[1], erp->rRK_len); if (hmac_sha256_kdf(erp->rRK, erp->rRK_len, - "EAP Re-authentication Integrity Key@ietf.org", - len, sizeof(len), erp->rIK, erp->rRK_len) < 0) { + "Re-authentication Integrity Key@ietf.org", + ctx, sizeof(ctx), erp->rIK, erp->rRK_len) < 0) { wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP"); goto fail; } @@ -563,7 +778,11 @@ static void eap_peer_erp_init(struct eap_sm *sm) dl_list_add(&sm->erp_keys, &erp->list); erp = NULL; fail: - bin_clear_free(emsk, emsk_len); + if (ext_emsk) + bin_clear_free(ext_emsk, ext_emsk_len); + else + bin_clear_free(emsk, emsk_len); + bin_clear_free(ext_session_id, ext_session_id_len); bin_clear_free(erp, sizeof(*erp)); os_free(realm); #endif /* CONFIG_ERP */ @@ -571,8 +790,7 @@ fail: #ifdef CONFIG_ERP -static int eap_peer_erp_reauth_start(struct eap_sm *sm, - const struct eap_hdr *hdr, size_t len) +struct wpabuf * eap_peer_build_erp_reauth_start(struct eap_sm *sm, u8 eap_id) { char *realm; struct eap_erp_key *erp; @@ -581,16 +799,16 @@ static int eap_peer_erp_reauth_start(struct eap_sm *sm, realm = eap_home_realm(sm); if (!realm) - return -1; + return NULL; erp = eap_erp_get_key(sm, realm); os_free(realm); realm = NULL; if (!erp) - return -1; + return NULL; if (erp->next_seq >= 65536) - return -1; /* SEQ has range of 0..65535 */ + return NULL; /* SEQ has range of 0..65535 */ /* TODO: check rRK lifetime expiration */ @@ -599,9 +817,9 @@ static int eap_peer_erp_reauth_start(struct eap_sm *sm, msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH, 1 + 2 + 2 + os_strlen(erp->keyname_nai) + 1 + 16, - EAP_CODE_INITIATE, hdr->identifier); + EAP_CODE_INITIATE, eap_id); if (msg == NULL) - return -1; + return NULL; wpabuf_put_u8(msg, 0x20); /* Flags: R=0 B=0 L=1 */ wpabuf_put_be16(msg, erp->next_seq); @@ -615,13 +833,28 @@ static int eap_peer_erp_reauth_start(struct eap_sm *sm, if (hmac_sha256(erp->rIK, erp->rIK_len, wpabuf_head(msg), wpabuf_len(msg), hash) < 0) { wpabuf_free(msg); - return -1; + return NULL; } wpabuf_put_data(msg, hash, 16); - wpa_printf(MSG_DEBUG, "EAP: Sending EAP-Initiate/Re-auth"); sm->erp_seq = erp->next_seq; erp->next_seq++; + + wpa_hexdump_buf(MSG_DEBUG, "ERP: EAP-Initiate/Re-auth", msg); + + return msg; +} + + +static int eap_peer_erp_reauth_start(struct eap_sm *sm, u8 eap_id) +{ + struct wpabuf *msg; + + msg = eap_peer_build_erp_reauth_start(sm, eap_id); + if (!msg) + return -1; + + wpa_printf(MSG_DEBUG, "EAP: Sending EAP-Initiate/Re-auth"); wpabuf_free(sm->eapRespData); sm->eapRespData = msg; sm->reauthInit = TRUE; @@ -691,8 +924,6 @@ SM_STATE(EAP, METHOD) if (sm->m->isKeyAvailable && sm->m->getKey && sm->m->isKeyAvailable(sm, sm->eap_method_priv)) { - struct eap_peer_config *config = eap_get_config(sm); - eap_sm_free_key(sm); sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, &sm->eapKeyDataLen); @@ -705,8 +936,6 @@ SM_STATE(EAP, METHOD) wpa_hexdump(MSG_DEBUG, "EAP: Session-Id", sm->eapSessionId, sm->eapSessionIdLen); } - if (config->erp && sm->m->get_emsk && sm->eapSessionId) - eap_peer_erp_init(sm); } } @@ -804,6 +1033,8 @@ SM_STATE(EAP, RETRANSMIT) */ SM_STATE(EAP, SUCCESS) { + struct eap_peer_config *config = eap_get_config(sm); + SM_ENTRY(EAP, SUCCESS); if (sm->eapKeyData != NULL) sm->eapKeyAvailable = TRUE; @@ -826,6 +1057,11 @@ SM_STATE(EAP, SUCCESS) wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS "EAP authentication completed successfully"); + + if (config->erp && sm->m->get_emsk && sm->eapSessionId && + sm->m->isKeyAvailable && + sm->m->isKeyAvailable(sm, sm->eap_method_priv)) + eap_peer_erp_init(sm, NULL, 0, NULL, 0); } @@ -1276,48 +1512,6 @@ static int mnc_len_from_imsi(const char *imsi) } -static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi, - size_t max_len, size_t *imsi_len) -{ - int mnc_len; - char *pos, mnc[4]; - - if (*imsi_len + 36 > max_len) { - wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer"); - return -1; - } - - /* MNC (2 or 3 digits) */ - mnc_len = scard_get_mnc_len(sm->scard_ctx); - if (mnc_len < 0) - mnc_len = mnc_len_from_imsi(imsi); - if (mnc_len < 0) { - wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM " - "assuming 3"); - mnc_len = 3; - } - - if (mnc_len == 2) { - mnc[0] = '0'; - mnc[1] = imsi[3]; - mnc[2] = imsi[4]; - } else if (mnc_len == 3) { - mnc[0] = imsi[3]; - mnc[1] = imsi[4]; - mnc[2] = imsi[5]; - } - mnc[3] = '\0'; - - pos = imsi + *imsi_len; - pos += os_snprintf(pos, imsi + max_len - pos, - "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org", - mnc, imsi[0], imsi[1], imsi[2]); - *imsi_len = pos - imsi; - - return 0; -} - - static int eap_sm_imsi_identity(struct eap_sm *sm, struct eap_peer_config *conf) { @@ -1325,7 +1519,7 @@ static int eap_sm_imsi_identity(struct eap_sm *sm, char imsi[100]; size_t imsi_len; struct eap_method_type *m = conf->eap_methods; - int i; + int i, mnc_len; imsi_len = sizeof(imsi); if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) { @@ -1340,7 +1534,18 @@ static int eap_sm_imsi_identity(struct eap_sm *sm, return -1; } - if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len) < 0) { + /* MNC (2 or 3 digits) */ + mnc_len = scard_get_mnc_len(sm->scard_ctx); + if (mnc_len < 0) + mnc_len = mnc_len_from_imsi(imsi); + if (mnc_len < 0) { + wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM " + "assuming 3"); + mnc_len = 3; + } + + if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len, + mnc_len) < 0) { wpa_printf(MSG_WARNING, "Could not add realm to SIM identity"); return -1; } @@ -1566,7 +1771,7 @@ static void eap_peer_initiate(struct eap_sm *sm, const struct eap_hdr *hdr, /* TODO: Derivation of domain specific keys for local ER */ } - if (eap_peer_erp_reauth_start(sm, hdr, len) == 0) + if (eap_peer_erp_reauth_start(sm, hdr->identifier) == 0) return; invalid: @@ -1577,8 +1782,7 @@ invalid: } -static void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr, - size_t len) +void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr, size_t len) { #ifdef CONFIG_ERP const u8 *pos = (const u8 *) (hdr + 1); @@ -1828,6 +2032,15 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) case EAP_CODE_FAILURE: wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure"); eap_notify_status(sm, "completion", "failure"); + + /* Get the error code from method */ + if (sm->m && sm->m->get_error_code) { + int error_code; + + error_code = sm->m->get_error_code(sm->eap_method_priv); + if (error_code != NO_EAP_METHOD_ERROR) + eap_report_error(sm, error_code); + } sm->rxFailure = TRUE; break; case EAP_CODE_INITIATE: @@ -2231,6 +2444,7 @@ static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, config->pending_req_passphrase++; break; case WPA_CTRL_REQ_SIM: + config->pending_req_sim++; txt = msg; break; case WPA_CTRL_REQ_EXT_CERT_CHECK: diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h index 1a645af8b200..d0837e37a0e0 100644 --- a/src/eap_peer/eap.h +++ b/src/eap_peer/eap.h @@ -246,12 +246,37 @@ struct eapol_callbacks { void (*notify_status)(void *ctx, const char *status, const char *parameter); + /** + * notify_eap_error - Report EAP method error code + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @error_code: Error code from the used EAP method + */ + void (*notify_eap_error)(void *ctx, int error_code); + #ifdef CONFIG_EAP_PROXY /** * eap_proxy_cb - Callback signifying any updates from eap_proxy * @ctx: eapol_ctx from eap_peer_sm_init() call */ void (*eap_proxy_cb)(void *ctx); + + /** + * eap_proxy_notify_sim_status - Notification of SIM status change + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @sim_state: One of enum value from sim_state + */ + void (*eap_proxy_notify_sim_status)(void *ctx, + enum eap_proxy_sim_state sim_state); + + /** + * get_imsi - Get the IMSI value from eap_proxy + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @sim_num: SIM/USIM number to get the IMSI value for + * @imsi: Buffer for IMSI value + * @len: Buffer for returning IMSI length in octets + * Returns: MNC length (2 or 3) or -1 on error + */ + int (*get_imsi)(void *ctx, int sim_num, char *imsi, size_t *len); #endif /* CONFIG_EAP_PROXY */ /** @@ -348,6 +373,16 @@ void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext); void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len); int eap_peer_was_failure_expected(struct eap_sm *sm); void eap_peer_erp_free_keys(struct eap_sm *sm); +struct wpabuf * eap_peer_build_erp_reauth_start(struct eap_sm *sm, u8 eap_id); +void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr, size_t len); +int eap_peer_get_erp_info(struct eap_sm *sm, struct eap_peer_config *config, + const u8 **username, size_t *username_len, + const u8 **realm, size_t *realm_len, u16 *erp_seq_num, + const u8 **rrk, size_t *rrk_len); +int eap_peer_update_erp_next_seq_num(struct eap_sm *sm, u16 seq_num); +void eap_peer_erp_init(struct eap_sm *sm, u8 *ext_session_id, + size_t ext_session_id_len, u8 *ext_emsk, + size_t ext_emsk_len); #endif /* IEEE8021X_EAPOL */ diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c index 0bac62dee523..a4441413f0dd 100644 --- a/src/eap_peer/eap_aka.c +++ b/src/eap_peer/eap_aka.c @@ -48,11 +48,15 @@ struct eap_aka_data { struct wpabuf *id_msgs; int prev_id; int result_ind, use_result_ind; + int use_pseudonym; u8 eap_method; u8 *network_name; size_t network_name_len; u16 kdf; int kdf_negotiation; + u16 last_kdf_attrs[EAP_AKA_PRIME_KDF_MAX]; + size_t last_kdf_count; + int error_code; }; @@ -96,12 +100,16 @@ static void * eap_aka_init(struct eap_sm *sm) data->eap_method = EAP_TYPE_AKA; + /* Zero is a valid error code, so we need to initialize */ + data->error_code = NO_EAP_METHOD_ERROR; + eap_aka_state(data, CONTINUE); data->prev_id = -1; data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL; - if (config && config->anonymous_identity) { + data->use_pseudonym = !sm->init_phase2; + if (config && config->anonymous_identity && data->use_pseudonym) { data->pseudonym = os_malloc(config->anonymous_identity_len); if (data->pseudonym) { os_memcpy(data->pseudonym, config->anonymous_identity, @@ -350,7 +358,8 @@ static void eap_aka_clear_identities(struct eap_sm *sm, os_free(data->pseudonym); data->pseudonym = NULL; data->pseudonym_len = 0; - eap_set_anon_id(sm, NULL, 0); + if (data->use_pseudonym) + eap_set_anon_id(sm, NULL, 0); } if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id"); @@ -405,20 +414,21 @@ static int eap_aka_learn_ids(struct eap_sm *sm, struct eap_aka_data *data, realm, realm_len); } data->pseudonym_len = attr->next_pseudonym_len + realm_len; - eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len); + if (data->use_pseudonym) + eap_set_anon_id(sm, data->pseudonym, + data->pseudonym_len); } if (attr->next_reauth_id) { os_free(data->reauth_id); - data->reauth_id = os_malloc(attr->next_reauth_id_len); + data->reauth_id = os_memdup(attr->next_reauth_id, + attr->next_reauth_id_len); if (data->reauth_id == NULL) { wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " "next reauth_id"); data->reauth_id_len = 0; return -1; } - os_memcpy(data->reauth_id, attr->next_reauth_id, - attr->next_reauth_id_len); data->reauth_id_len = attr->next_reauth_id_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: (encr) AT_NEXT_REAUTH_ID", @@ -570,7 +580,7 @@ static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data, static struct wpabuf * eap_aka_synchronization_failure( - struct eap_aka_data *data, u8 id) + struct eap_aka_data *data, u8 id, struct eap_sim_attrs *attr) { struct eap_sim_msg *msg; @@ -584,6 +594,15 @@ static struct wpabuf * eap_aka_synchronization_failure( wpa_printf(MSG_DEBUG, " AT_AUTS"); eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts, EAP_AKA_AUTS_LEN); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + size_t i; + + for (i = 0; i < attr->kdf_count; i++) { + wpa_printf(MSG_DEBUG, " AT_KDF"); + eap_sim_msg_add(msg, EAP_SIM_AT_KDF, attr->kdf[i], + NULL, 0); + } + } return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0); } @@ -817,9 +836,13 @@ static struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data, size_t i; for (i = 0; i < attr->kdf_count; i++) { - if (attr->kdf[i] == EAP_AKA_PRIME_KDF) + if (attr->kdf[i] == EAP_AKA_PRIME_KDF) { + os_memcpy(data->last_kdf_attrs, attr->kdf, + sizeof(u16) * attr->kdf_count); + data->last_kdf_count = attr->kdf_count; return eap_aka_prime_kdf_select(data, id, EAP_AKA_PRIME_KDF); + } } /* No matching KDF found - fail authentication as if AUTN had been @@ -840,26 +863,32 @@ static int eap_aka_prime_kdf_valid(struct eap_aka_data *data, * of the selected KDF into the beginning of the list. */ if (data->kdf_negotiation) { + /* When the peer receives the new EAP-Request/AKA'-Challenge + * message, must check only requested change occurred in the + * list of AT_KDF attributes. If there are any other changes, + * the peer must behave like the case that AT_MAC had been + * incorrect and authentication is failed. These are defined in + * EAP-AKA' specification RFC 5448, Section 3.2. */ if (attr->kdf[0] != data->kdf) { wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " "accept the selected KDF"); - return 0; + return -1; } - for (i = 1; i < attr->kdf_count; i++) { - if (attr->kdf[i] == data->kdf) - break; - } - if (i == attr->kdf_count && - attr->kdf_count < EAP_AKA_PRIME_KDF_MAX) { - wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " - "duplicate the selected KDF"); - return 0; + if (attr->kdf_count > EAP_AKA_PRIME_KDF_MAX || + attr->kdf_count != data->last_kdf_count + 1) { + wpa_printf(MSG_WARNING, + "EAP-AKA': The length of KDF attributes is wrong"); + return -1; } - /* TODO: should check that the list is identical to the one - * used in the previous Challenge message apart from the added - * entry in the beginning. */ + for (i = 1; i < attr->kdf_count; i++) { + if (attr->kdf[i] != data->last_kdf_attrs[i - 1]) { + wpa_printf(MSG_WARNING, + "EAP-AKA': The KDF attributes except selected KDF are not same as original one"); + return -1; + } + } } for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) { @@ -908,22 +937,25 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, return eap_aka_authentication_reject(data, id); } os_free(data->network_name); - data->network_name = os_malloc(attr->kdf_input_len); + data->network_name = os_memdup(attr->kdf_input, + attr->kdf_input_len); if (data->network_name == NULL) { wpa_printf(MSG_WARNING, "EAP-AKA': No memory for " "storing Network Name"); return eap_aka_authentication_reject(data, id); } - os_memcpy(data->network_name, attr->kdf_input, - attr->kdf_input_len); data->network_name_len = attr->kdf_input_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name " "(AT_KDF_INPUT)", data->network_name, data->network_name_len); /* TODO: check Network Name per 3GPP.33.402 */ - if (!eap_aka_prime_kdf_valid(data, attr)) + res = eap_aka_prime_kdf_valid(data, attr); + if (res == 0) return eap_aka_authentication_reject(data, id); + else if (res == -1) + return eap_aka_client_error( + data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); if (attr->kdf[0] != EAP_AKA_PRIME_KDF) return eap_aka_prime_kdf_neg(data, id, attr); @@ -966,7 +998,7 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, } else if (res == -2) { wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " "failed (AUTN seq# -> AUTS)"); - return eap_aka_synchronization_failure(data, id); + return eap_aka_synchronization_failure(data, id, attr); } else if (res > 0) { wpa_printf(MSG_DEBUG, "EAP-AKA: Wait for external USIM processing"); return NULL; @@ -997,8 +1029,17 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, } else if (data->pseudonym) { identity = data->pseudonym; identity_len = data->pseudonym_len; - } else - identity = eap_get_config_identity(sm, &identity_len); + } else { + struct eap_peer_config *config; + + config = eap_get_config(sm); + if (config && config->imsi_identity) { + identity = config->imsi_identity; + identity_len = config->imsi_identity_len; + } else { + identity = eap_get_config_identity(sm, &identity_len); + } + } wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK " "derivation", identity, identity_len); if (data->eap_method == EAP_TYPE_AKA_PRIME) { @@ -1143,6 +1184,7 @@ static struct wpabuf * eap_aka_process_notification( eap_sim_report_notification(sm->msg_ctx, attr->notification, 1); if (attr->notification >= 0 && attr->notification < 32768) { + data->error_code = attr->notification; eap_aka_state(data, FAILURE); } else if (attr->notification == EAP_SIM_SUCCESS && data->state == RESULT_SUCCESS) @@ -1437,12 +1479,11 @@ static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_SIM_KEYING_DATA_LEN); + key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN); if (key == NULL) return NULL; *len = EAP_SIM_KEYING_DATA_LEN; - os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); return key; } @@ -1478,17 +1519,33 @@ static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); return key; } +static int eap_aka_get_error_code(void *priv) +{ + struct eap_aka_data *data = priv; + int current_data_error; + + if (!data) + return NO_EAP_METHOD_ERROR; + + current_data_error = data->error_code; + + /* Now reset for next transaction */ + data->error_code = NO_EAP_METHOD_ERROR; + + return current_data_error; +} + + int eap_peer_aka_register(void) { struct eap_method *eap; @@ -1509,6 +1566,7 @@ int eap_peer_aka_register(void) eap->init_for_reauth = eap_aka_init_for_reauth; eap->get_identity = eap_aka_get_identity; eap->get_emsk = eap_aka_get_emsk; + eap->get_error_code = eap_aka_get_error_code; return eap_peer_method_register(eap); } @@ -1536,6 +1594,7 @@ int eap_peer_aka_prime_register(void) eap->init_for_reauth = eap_aka_init_for_reauth; eap->get_identity = eap_aka_get_identity; eap->get_emsk = eap_aka_get_emsk; + eap->get_error_code = eap_aka_get_error_code; return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h index f98007263b33..d416afd56d59 100644 --- a/src/eap_peer/eap_config.h +++ b/src/eap_peer/eap_config.h @@ -46,6 +46,9 @@ struct eap_peer_config { */ size_t anonymous_identity_len; + u8 *imsi_identity; + size_t imsi_identity_len; + /** * password - Password string for EAP * @@ -628,6 +631,15 @@ struct eap_peer_config { int pending_req_passphrase; /** + * pending_req_sim - Pending SIM request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_sim; + + /** * pending_req_otp - Whether there is a pending OTP request * * This field should not be set in configuration step. It is only used diff --git a/src/eap_peer/eap_eke.c b/src/eap_peer/eap_eke.c index f899f653fdca..0de7d6cbf49b 100644 --- a/src/eap_peer/eap_eke.c +++ b/src/eap_peer/eap_eke.c @@ -85,12 +85,11 @@ static void * eap_eke_init(struct eap_sm *sm) identity = eap_get_config_identity(sm, &identity_len); if (identity) { - data->peerid = os_malloc(identity_len); + data->peerid = os_memdup(identity, identity_len); if (data->peerid == NULL) { eap_eke_deinit(sm, data); return NULL; } - os_memcpy(data->peerid, identity, identity_len); data->peerid_len = identity_len; } @@ -310,12 +309,11 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Server Identity", pos, end - pos); os_free(data->serverid); - data->serverid = os_malloc(end - pos); + data->serverid = os_memdup(pos, end - pos); if (data->serverid == NULL) { return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } - os_memcpy(data->serverid, pos, end - pos); data->serverid_len = end - pos; wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-ID/Response"); @@ -717,10 +715,9 @@ static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -735,10 +732,9 @@ static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c index 964ebe74fede..74cec7dd583f 100644 --- a/src/eap_peer/eap_fast.c +++ b/src/eap_peer/eap_fast.c @@ -484,7 +484,8 @@ static int eap_fast_phase2_request(struct eap_sm *sm, if (*resp == NULL && config && (config->pending_req_identity || config->pending_req_password || - config->pending_req_otp || config->pending_req_new_password)) { + config->pending_req_otp || config->pending_req_new_password || + config->pending_req_sim)) { wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); } else if (*resp == NULL) @@ -1677,6 +1678,10 @@ static Boolean eap_fast_has_reauth_data(struct eap_sm *sm, void *priv) static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv) { struct eap_fast_data *data = priv; + + if (data->phase2_priv && data->phase2_method && + data->phase2_method->deinit_for_reauth) + data->phase2_method->deinit_for_reauth(sm, data->phase2_priv); os_free(data->key_block_p); data->key_block_p = NULL; wpabuf_free(data->pending_phase2_req); @@ -1744,12 +1749,11 @@ static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len) if (!data->success) return NULL; - key = os_malloc(EAP_FAST_KEY_LEN); + key = os_memdup(data->key_data, EAP_FAST_KEY_LEN); if (key == NULL) return NULL; *len = EAP_FAST_KEY_LEN; - os_memcpy(key, data->key_data, EAP_FAST_KEY_LEN); return key; } @@ -1763,12 +1767,11 @@ static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (!data->success || !data->session_id) return NULL; - id = os_malloc(data->id_len); + id = os_memdup(data->session_id, data->id_len); if (id == NULL) return NULL; *len = data->id_len; - os_memcpy(id, data->session_id, data->id_len); return id; } @@ -1782,12 +1785,11 @@ static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (!data->success) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); return key; } diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c index c81586035513..7d674c8c0a70 100644 --- a/src/eap_peer/eap_fast_pac.c +++ b/src/eap_peer/eap_fast_pac.c @@ -114,10 +114,9 @@ static int eap_fast_copy_buf(u8 **dst, size_t *dst_len, const u8 *src, size_t src_len) { if (src) { - *dst = os_malloc(src_len); + *dst = os_memdup(src, src_len); if (*dst == NULL) return -1; - os_memcpy(*dst, src, src_len); *dst_len = src_len; } return 0; @@ -720,19 +719,17 @@ static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac) if (type == PAC_TYPE_A_ID) { os_free(pac->a_id); - pac->a_id = os_malloc(len); + pac->a_id = os_memdup(pos, len); if (pac->a_id == NULL) break; - os_memcpy(pac->a_id, pos, len); pac->a_id_len = len; } if (type == PAC_TYPE_A_ID_INFO) { os_free(pac->a_id_info); - pac->a_id_info = os_malloc(len); + pac->a_id_info = os_memdup(pos, len); if (pac->a_id_info == NULL) break; - os_memcpy(pac->a_id_info, pos, len); pac->a_id_info_len = len; } @@ -820,10 +817,9 @@ int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root, if (val > end - pos) goto parse_fail; pac->pac_opaque_len = val; - pac->pac_opaque = os_malloc(pac->pac_opaque_len); + pac->pac_opaque = os_memdup(pos, pac->pac_opaque_len); if (pac->pac_opaque == NULL) goto parse_fail; - os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len); pos += pac->pac_opaque_len; if (2 > end - pos) goto parse_fail; @@ -832,10 +828,9 @@ int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root, if (val > end - pos) goto parse_fail; pac->pac_info_len = val; - pac->pac_info = os_malloc(pac->pac_info_len); + pac->pac_info = os_memdup(pos, pac->pac_info_len); if (pac->pac_info == NULL) goto parse_fail; - os_memcpy(pac->pac_info, pos, pac->pac_info_len); pos += pac->pac_info_len; eap_fast_pac_get_a_id(pac); diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c index 177cbccf5850..f9c4d3773bf7 100644 --- a/src/eap_peer/eap_gpsk.c +++ b/src/eap_peer/eap_gpsk.c @@ -96,12 +96,11 @@ static void * eap_gpsk_init(struct eap_sm *sm) identity = eap_get_config_identity(sm, &identity_len); if (identity) { - data->id_peer = os_malloc(identity_len); + data->id_peer = os_memdup(identity, identity_len); if (data->id_peer == NULL) { eap_gpsk_deinit(sm, data); return NULL; } - os_memcpy(data->id_peer, identity, identity_len); data->id_peer_len = identity_len; } @@ -117,12 +116,11 @@ static void * eap_gpsk_init(struct eap_sm *sm) } } - data->psk = os_malloc(password_len); + data->psk = os_memdup(password, password_len); if (data->psk == NULL) { eap_gpsk_deinit(sm, data); return NULL; } - os_memcpy(data->psk, password, password_len); data->psk_len = password_len; return data; @@ -158,12 +156,11 @@ static const u8 * eap_gpsk_process_id_server(struct eap_gpsk_data *data, return NULL; } os_free(data->id_server); - data->id_server = os_malloc(alen); + data->id_server = os_memdup(pos, alen); if (data->id_server == NULL) { wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server"); return NULL; } - os_memcpy(data->id_server, pos, alen); data->id_server_len = alen; wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server", data->id_server, data->id_server_len); @@ -722,10 +719,9 @@ static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -740,10 +736,9 @@ static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; @@ -758,10 +753,9 @@ static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - sid = os_malloc(data->id_len); + sid = os_memdup(data->session_id, data->id_len); if (sid == NULL) return NULL; - os_memcpy(sid, data->session_id, data->id_len); *len = data->id_len; return sid; diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h index 6ab24834d654..096f0f28efca 100644 --- a/src/eap_peer/eap_i.h +++ b/src/eap_peer/eap_i.h @@ -14,6 +14,8 @@ #include "eap_peer/eap.h" #include "eap_common/eap_common.h" +#define NO_EAP_METHOD_ERROR (-1) + /* RFC 4137 - EAP Peer state machine */ typedef enum { @@ -206,6 +208,17 @@ struct eap_method { const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len); /** + * get_error_code - Get the latest EAP method error code + * @priv: Pointer to private EAP method data from eap_method::init() + * Returns: An int for the EAP method specific error code if exists or + * NO_EAP_METHOD_ERROR otherwise. + * + * This method is an optional handler that only EAP methods that need to + * report their error code need to implement. + */ + int (*get_error_code)(void *priv); + + /** * free - Free EAP method data * @method: Pointer to the method data registered with * eap_peer_method_register(). diff --git a/src/eap_peer/eap_ikev2.c b/src/eap_peer/eap_ikev2.c index 390f0ec8cf4d..6ddf50835ade 100644 --- a/src/eap_peer/eap_ikev2.c +++ b/src/eap_peer/eap_ikev2.c @@ -83,18 +83,16 @@ static void * eap_ikev2_init(struct eap_sm *sm) if (data->ikev2.key_pad == NULL) goto failed; data->ikev2.key_pad_len = 21; - data->ikev2.IDr = os_malloc(identity_len); + data->ikev2.IDr = os_memdup(identity, identity_len); if (data->ikev2.IDr == NULL) goto failed; - os_memcpy(data->ikev2.IDr, identity, identity_len); data->ikev2.IDr_len = identity_len; password = eap_get_config_password(sm, &password_len); if (password) { - data->ikev2.shared_secret = os_malloc(password_len); + data->ikev2.shared_secret = os_memdup(password, password_len); if (data->ikev2.shared_secret == NULL) goto failed; - os_memcpy(data->ikev2.shared_secret, password, password_len); data->ikev2.shared_secret_len = password_len; } diff --git a/src/eap_peer/eap_leap.c b/src/eap_peer/eap_leap.c index ff6fa4afd2f7..233b9eeb1f83 100644 --- a/src/eap_peer/eap_leap.c +++ b/src/eap_peer/eap_leap.c @@ -115,10 +115,14 @@ static struct wpabuf * eap_leap_process_request(struct eap_sm *sm, void *priv, wpabuf_put_u8(resp, 0); /* unused */ wpabuf_put_u8(resp, LEAP_RESPONSE_LEN); rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN); - if (pwhash) - challenge_response(challenge, password, rpos); - else - nt_challenge_response(challenge, password, password_len, rpos); + if ((pwhash && challenge_response(challenge, password, rpos)) || + (!pwhash && + nt_challenge_response(challenge, password, password_len, rpos))) { + wpa_printf(MSG_DEBUG, "EAP-LEAP: Failed to derive response"); + ret->ignore = TRUE; + wpabuf_free(resp); + return NULL; + } os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response", rpos, LEAP_RESPONSE_LEN); @@ -239,7 +243,10 @@ static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv, return NULL; } } - challenge_response(data->ap_challenge, pw_hash_hash, expected); + if (challenge_response(data->ap_challenge, pw_hash_hash, expected)) { + ret->ignore = TRUE; + return NULL; + } ret->methodState = METHOD_DONE; ret->allowNotifications = FALSE; diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c index ce2227d388ef..877495cf3ac7 100644 --- a/src/eap_peer/eap_mschapv2.c +++ b/src/eap_peer/eap_mschapv2.c @@ -109,23 +109,21 @@ static void * eap_mschapv2_init(struct eap_sm *sm) return NULL; if (sm->peer_challenge) { - data->peer_challenge = os_malloc(MSCHAPV2_CHAL_LEN); + data->peer_challenge = os_memdup(sm->peer_challenge, + MSCHAPV2_CHAL_LEN); if (data->peer_challenge == NULL) { eap_mschapv2_deinit(sm, data); return NULL; } - os_memcpy(data->peer_challenge, sm->peer_challenge, - MSCHAPV2_CHAL_LEN); } if (sm->auth_challenge) { - data->auth_challenge = os_malloc(MSCHAPV2_CHAL_LEN); + data->auth_challenge = os_memdup(sm->auth_challenge, + MSCHAPV2_CHAL_LEN); if (data->auth_challenge == NULL) { eap_mschapv2_deinit(sm, data); return NULL; } - os_memcpy(data->auth_challenge, sm->auth_challenge, - MSCHAPV2_CHAL_LEN); } data->phase2 = sm->init_phase2; @@ -567,11 +565,11 @@ static struct wpabuf * eap_mschapv2_change_password( if (pwhash) { u8 new_password_hash[16]; if (nt_password_hash(new_password, new_password_len, - new_password_hash)) + new_password_hash) || + nt_password_hash_encrypted_with_block(password, + new_password_hash, + cp->encr_hash)) goto fail; - nt_password_hash_encrypted_with_block(password, - new_password_hash, - cp->encr_hash); } else { if (old_nt_password_hash_encrypted_with_new_nt_password_hash( new_password, new_password_len, diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c index a7012d2870cf..3cef1c8800a2 100644 --- a/src/eap_peer/eap_pax.c +++ b/src/eap_peer/eap_pax.c @@ -69,12 +69,11 @@ static void * eap_pax_init(struct eap_sm *sm) return NULL; data->state = PAX_INIT; - data->cid = os_malloc(identity_len); + data->cid = os_memdup(identity, identity_len); if (data->cid == NULL) { eap_pax_deinit(sm, data); return NULL; } - os_memcpy(data->cid, identity, identity_len); data->cid_len = identity_len; os_memcpy(data->ak, password, EAP_PAX_AK_LEN); diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c index 45ba38168d4f..34075b1d9af6 100644 --- a/src/eap_peer/eap_peap.c +++ b/src/eap_peer/eap_peap.c @@ -726,7 +726,8 @@ static int eap_peap_phase2_request(struct eap_sm *sm, if (*resp == NULL && (config->pending_req_identity || config->pending_req_password || - config->pending_req_otp || config->pending_req_new_password)) { + config->pending_req_otp || config->pending_req_new_password || + config->pending_req_sim)) { wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); } @@ -1082,7 +1083,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, eap_peer_tls_derive_key(sm, &data->ssl, label, EAP_TLS_KEY_LEN); if (data->key_data) { - wpa_hexdump_key(MSG_DEBUG, + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Derived key", data->key_data, EAP_TLS_KEY_LEN); @@ -1163,6 +1164,10 @@ static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv) static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv) { struct eap_peap_data *data = priv; + + if (data->phase2_priv && data->phase2_method && + data->phase2_method->deinit_for_reauth) + data->phase2_method->deinit_for_reauth(sm, data->phase2_priv); wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = NULL; wpabuf_free(data->pending_resp); @@ -1267,12 +1272,11 @@ static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (data->session_id == NULL || !data->phase2_success) return NULL; - id = os_malloc(data->id_len); + id = os_memdup(data->session_id, data->id_len); if (id == NULL) return NULL; *len = data->id_len; - os_memcpy(id, data->session_id, data->id_len); return id; } diff --git a/src/eap_peer/eap_proxy.h b/src/eap_peer/eap_proxy.h index 23cdbe698b3c..9d8e57026722 100644 --- a/src/eap_peer/eap_proxy.h +++ b/src/eap_peer/eap_proxy.h @@ -20,7 +20,7 @@ enum eap_proxy_status { }; struct eap_proxy_sm * -eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, +eap_proxy_init(void *eapol_ctx, const struct eapol_callbacks *eapol_cb, void *msg_ctx); void eap_proxy_deinit(struct eap_proxy_sm *eap_proxy); @@ -40,10 +40,16 @@ eap_proxy_packet_update(struct eap_proxy_sm *eap_proxy, u8 *eapReqData, int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen, int verbose); -int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf, - size_t *imsi_len); +int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, int sim_num, + char *imsi_buf, size_t *imsi_len); int eap_proxy_notify_config(struct eap_proxy_sm *sm, struct eap_peer_config *config); +u8 * eap_proxy_get_eap_session_id(struct eap_proxy_sm *sm, size_t *len); + +u8 * eap_proxy_get_emsk(struct eap_proxy_sm *sm, size_t *len); + +void eap_proxy_sm_abort(struct eap_proxy_sm *sm); + #endif /* EAP_PROXY_H */ diff --git a/src/eap_peer/eap_proxy_dummy.c b/src/eap_peer/eap_proxy_dummy.c index d84f01234ed5..2cc05c92cdfc 100644 --- a/src/eap_peer/eap_proxy_dummy.c +++ b/src/eap_peer/eap_proxy_dummy.c @@ -12,7 +12,7 @@ #include "eap_proxy.h" struct eap_proxy_sm * -eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, +eap_proxy_init(void *eapol_ctx, const struct eapol_callbacks *eapol_cb, void *msg_ctx) { return NULL; @@ -63,8 +63,8 @@ int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen, } -int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf, - size_t *imsi_len) +int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, int sim_num, + char *imsi_buf, size_t *imsi_len) { return -1; } @@ -75,3 +75,20 @@ int eap_proxy_notify_config(struct eap_proxy_sm *sm, { return -1; } + + +u8 * eap_proxy_get_eap_session_id(struct eap_proxy_sm *sm, size_t *len) +{ + return NULL; +} + + +u8 * eap_proxy_get_emsk(struct eap_proxy_sm *sm, size_t *len) +{ + return NULL; +} + + +void eap_proxy_sm_abort(struct eap_proxy_sm *sm) +{ +} diff --git a/src/eap_peer/eap_psk.c b/src/eap_peer/eap_psk.c index ac18c158ad8b..eea9430d2406 100644 --- a/src/eap_peer/eap_psk.c +++ b/src/eap_peer/eap_psk.c @@ -116,14 +116,13 @@ static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data, os_memcpy(data->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN); os_free(data->id_s); data->id_s_len = len - sizeof(*hdr1); - data->id_s = os_malloc(data->id_s_len); + data->id_s = os_memdup(hdr1 + 1, data->id_s_len); if (data->id_s == NULL) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for " "ID_S (len=%lu)", (unsigned long) data->id_s_len); ret->ignore = TRUE; return NULL; } - os_memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len); wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S", data->id_s, data->id_s_len); @@ -273,13 +272,12 @@ static struct wpabuf * eap_psk_process_3(struct eap_psk_data *data, wpabuf_head(reqData), 5); wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left); - decrypted = os_malloc(left); + decrypted = os_memdup(msg, left); if (decrypted == NULL) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return NULL; } - os_memcpy(decrypted, msg, left); if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), wpabuf_head(reqData), @@ -425,12 +423,11 @@ static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != PSK_DONE) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; *len = EAP_MSK_LEN; - os_memcpy(key, data->msk, EAP_MSK_LEN); return key; } @@ -466,12 +463,11 @@ static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != PSK_DONE) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); return key; } diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c index d2bc981cd06b..761c16af996a 100644 --- a/src/eap_peer/eap_pwd.c +++ b/src/eap_peer/eap_pwd.c @@ -9,8 +9,11 @@ #include "includes.h" #include "common.h" +#include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/sha512.h" #include "crypto/ms_funcs.h" +#include "crypto/crypto.h" #include "eap_peer/eap_i.h" #include "eap_common/eap_pwd_common.h" @@ -28,6 +31,8 @@ struct eap_pwd_data { size_t password_len; int password_hash; u16 group_num; + u8 prep; + u8 token[4]; EAP_PWD_group *grp; struct wpabuf *inbuf; @@ -36,18 +41,16 @@ struct eap_pwd_data { size_t out_frag_pos; size_t mtu; - BIGNUM *k; - BIGNUM *private_value; - BIGNUM *server_scalar; - BIGNUM *my_scalar; - EC_POINT *my_element; - EC_POINT *server_element; + struct crypto_bignum *k; + struct crypto_bignum *private_value; + struct crypto_bignum *server_scalar; + struct crypto_bignum *my_scalar; + struct crypto_ec_point *my_element; + struct crypto_ec_point *server_element; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; u8 session_id[1 + SHA256_MAC_LEN]; - - BN_CTX *bnctx; }; @@ -107,15 +110,8 @@ static void * eap_pwd_init(struct eap_sm *sm) return NULL; } - if ((data->bnctx = BN_CTX_new()) == NULL) { - wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail"); - os_free(data); - return NULL; - } - if ((data->id_peer = os_malloc(identity_len)) == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); - BN_CTX_free(data->bnctx); os_free(data); return NULL; } @@ -125,7 +121,6 @@ static void * eap_pwd_init(struct eap_sm *sm) if ((data->password = os_malloc(password_len)) == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail"); - BN_CTX_free(data->bnctx); bin_clear_free(data->id_peer, data->id_peer_len); os_free(data); return NULL; @@ -152,21 +147,18 @@ static void eap_pwd_deinit(struct eap_sm *sm, void *priv) { struct eap_pwd_data *data = priv; - BN_clear_free(data->private_value); - BN_clear_free(data->server_scalar); - BN_clear_free(data->my_scalar); - BN_clear_free(data->k); - BN_CTX_free(data->bnctx); - EC_POINT_clear_free(data->my_element); - EC_POINT_clear_free(data->server_element); + crypto_bignum_deinit(data->private_value, 1); + crypto_bignum_deinit(data->server_scalar, 1); + crypto_bignum_deinit(data->my_scalar, 1); + crypto_bignum_deinit(data->k, 1); + crypto_ec_point_deinit(data->my_element, 1); + crypto_ec_point_deinit(data->server_element, 1); bin_clear_free(data->id_peer, data->id_peer_len); bin_clear_free(data->id_server, data->id_server_len); bin_clear_free(data->password, data->password_len); if (data->grp) { - EC_GROUP_free(data->grp->group); - EC_POINT_clear_free(data->grp->pwe); - BN_clear_free(data->grp->order); - BN_clear_free(data->grp->prime); + crypto_ec_deinit(data->grp->group); + crypto_ec_point_deinit(data->grp->pwe, 1); os_free(data->grp); } wpabuf_free(data->inbuf); @@ -183,11 +175,10 @@ static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -202,11 +193,10 @@ static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - id = os_malloc(1 + SHA256_MAC_LEN); + id = os_memdup(data->session_id, 1 + SHA256_MAC_LEN); if (id == NULL) return NULL; - os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN); *len = 1 + SHA256_MAC_LEN; return id; @@ -220,10 +210,6 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { struct eap_pwd_id *id; - const u8 *password; - size_t password_len; - u8 pwhashhash[16]; - int res; if (data->state != PWD_ID_Req) { ret->ignore = TRUE; @@ -250,7 +236,10 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, } if (id->prep != EAP_PWD_PREP_NONE && - id->prep != EAP_PWD_PREP_MS) { + id->prep != EAP_PWD_PREP_MS && + id->prep != EAP_PWD_PREP_SSHA1 && + id->prep != EAP_PWD_PREP_SSHA256 && + id->prep != EAP_PWD_PREP_SSHA512) { wpa_printf(MSG_DEBUG, "EAP-PWD: Unsupported password pre-processing technique (Prep=%u)", id->prep); @@ -268,6 +257,15 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d", data->group_num); + data->prep = id->prep; + os_memcpy(data->token, id->token, sizeof(id->token)); + + if (data->id_server || data->grp) { + wpa_printf(MSG_INFO, "EAP-pwd: data was already allocated"); + eap_pwd_state(data, FAILURE); + return; + } + data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id)); if (data->id_server == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); @@ -279,7 +277,7 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of", data->id_server, data->id_server_len); - data->grp = os_zalloc(sizeof(EAP_PWD_group)); + data->grp = get_eap_pwd_group(data->group_num); if (data->grp == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for " "group"); @@ -287,13 +285,74 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, return; } - if (id->prep == EAP_PWD_PREP_MS) { + data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) + + data->id_peer_len); + if (data->outbuf == NULL) { + eap_pwd_state(data, FAILURE); + return; + } + wpabuf_put_be16(data->outbuf, data->group_num); + wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); + wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); + wpabuf_put_data(data->outbuf, id->token, sizeof(id->token)); + wpabuf_put_u8(data->outbuf, id->prep); + wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len); + + eap_pwd_state(data, PWD_Commit_Req); +} + + +static void +eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, size_t payload_len) +{ + struct crypto_ec_point *K = NULL, *point = NULL; + struct crypto_bignum *mask = NULL, *cofactor = NULL; + const u8 *ptr = payload; + u8 *scalar = NULL, *element = NULL; + size_t prime_len, order_len; + const u8 *password; + size_t password_len; + u8 pwhashhash[16]; + const u8 *salt_pwd[2]; + size_t salt_pwd_len[2], exp_len; + u8 salt_len, salthashpwd[64]; /* 64 = SHA512_DIGEST_LENGTH */ + int res; + + if (data->state != PWD_Commit_Req) { + ret->ignore = TRUE; + goto fin; + } + + if (!data->grp) { + wpa_printf(MSG_DEBUG, + "EAP-PWD (client): uninitialized EAP-pwd group"); + ret->ignore = TRUE; + goto fin; + } + + prime_len = crypto_ec_prime_len(data->grp->group); + order_len = crypto_ec_order_len(data->grp->group); + + switch (data->prep) { + case EAP_PWD_PREP_MS: + wpa_printf(MSG_DEBUG, + "EAP-pwd commit request, password prep is MS"); #ifdef CONFIG_FIPS wpa_printf(MSG_ERROR, "EAP-PWD (peer): MS password hash not supported in FIPS mode"); eap_pwd_state(data, FAILURE); return; #else /* CONFIG_FIPS */ + if (payload_len != 2 * prime_len + order_len) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Commit payload length %u (expected %u)", + (unsigned int) payload_len, + (unsigned int) (2 * prime_len + order_len)); + goto fin; + } if (data->password_hash) { res = hash_nt_password_hash(data->password, pwhashhash); } else { @@ -314,9 +373,139 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, password = pwhashhash; password_len = sizeof(pwhashhash); #endif /* CONFIG_FIPS */ - } else { + break; + case EAP_PWD_PREP_SSHA1: + wpa_printf(MSG_DEBUG, + "EAP-pwd commit request, password prep is salted sha1"); + if (payload_len < 1 || *ptr == 0) { + wpa_printf(MSG_DEBUG, "EAP-pwd: Invalid Salt-len"); + goto fin; + } + salt_len = *ptr++; + exp_len = 1 + salt_len + 2 * prime_len + order_len; + if (payload_len != exp_len) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Commit payload length %u (expected %u)", + (unsigned int) payload_len, + (unsigned int) exp_len); + goto fin; + } + + /* salted-password = Hash(password | salt) */ + wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Unsalted password", + data->password, data->password_len); + wpa_hexdump(MSG_DEBUG, "EAP-pwd: Salt", ptr, salt_len); + salt_pwd[0] = data->password; + salt_pwd[1] = ptr; + salt_pwd_len[0] = data->password_len; + salt_pwd_len[1] = salt_len; + if (sha1_vector(2, salt_pwd, salt_pwd_len, salthashpwd) < 0) + goto fin; + + wpa_printf(MSG_DEBUG, + "EAP-pwd: sha1 hashed %d byte salt with password", + (int) salt_len); + ptr += salt_len; + password = salthashpwd; + password_len = SHA1_MAC_LEN; + wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Salted password", + password, password_len); + break; + case EAP_PWD_PREP_SSHA256: + wpa_printf(MSG_DEBUG, + "EAP-pwd commit request, password prep is salted sha256"); + if (payload_len < 1 || *ptr == 0) { + wpa_printf(MSG_DEBUG, "EAP-pwd: Invalid Salt-len"); + goto fin; + } + salt_len = *ptr++; + exp_len = 1 + salt_len + 2 * prime_len + order_len; + if (payload_len != exp_len) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Commit payload length %u (expected %u)", + (unsigned int) payload_len, + (unsigned int) exp_len); + goto fin; + } + + /* salted-password = Hash(password | salt) */ + wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Unsalted password", + data->password, data->password_len); + wpa_hexdump(MSG_DEBUG, "EAP-pwd: Salt", ptr, salt_len); + salt_pwd[0] = data->password; + salt_pwd[1] = ptr; + salt_pwd_len[0] = data->password_len; + salt_pwd_len[1] = salt_len; + if (sha256_vector(2, salt_pwd, salt_pwd_len, salthashpwd) < 0) + goto fin; + + ptr += salt_len; + password = salthashpwd; + password_len = SHA256_MAC_LEN; + wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Salted password", + password, password_len); + break; +#ifdef CONFIG_SHA512 + case EAP_PWD_PREP_SSHA512: + wpa_printf(MSG_DEBUG, + "EAP-pwd commit request, password prep is salted sha512"); + if (payload_len < 1 || *ptr == 0) { + wpa_printf(MSG_DEBUG, "EAP-pwd: Invalid Salt-len"); + goto fin; + } + salt_len = *ptr++; + exp_len = 1 + salt_len + 2 * prime_len + order_len; + if (payload_len != exp_len) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Commit payload length %u (expected %u)", + (unsigned int) payload_len, + (unsigned int) exp_len); + goto fin; + } + + /* salted-password = Hash(password | salt) */ + wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Unsalted password", + data->password, data->password_len); + wpa_hexdump(MSG_DEBUG, "EAP-pwd: Salt", ptr, salt_len); + salt_pwd[0] = data->password; + salt_pwd[1] = ptr; + salt_pwd_len[0] = data->password_len; + salt_pwd_len[1] = salt_len; + if (sha512_vector(2, salt_pwd, salt_pwd_len, salthashpwd) < 0) + goto fin; + + ptr += salt_len; + password = salthashpwd; + password_len = SHA512_MAC_LEN; + wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Salted password", + password, password_len); + break; +#endif /* CONFIG_SHA512 */ + case EAP_PWD_PREP_NONE: + wpa_printf(MSG_DEBUG, + "EAP-pwd commit request, password prep is NONE"); + if (data->password_hash) { + wpa_printf(MSG_DEBUG, + "EAP-PWD: Unhashed password not available"); + eap_pwd_state(data, FAILURE); + return; + } + if (payload_len != 2 * prime_len + order_len) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Commit payload length %u (expected %u)", + (unsigned int) payload_len, + (unsigned int) (2 * prime_len + order_len)); + goto fin; + } password = data->password; password_len = data->password_len; + break; + default: + wpa_printf(MSG_DEBUG, + "EAP-pwd: Unsupported password pre-processing technique (Prep=%u)", + data->prep); + eap_pwd_state(data, FAILURE); + return; } /* compute PWE */ @@ -324,8 +513,9 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, password, password_len, data->id_server, data->id_server_len, data->id_peer, data->id_peer_len, - id->token); + data->token); os_memset(pwhashhash, 0, sizeof(pwhashhash)); + os_memset(salthashpwd, 0, sizeof(salthashpwd)); if (res) { wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE"); eap_pwd_state(data, FAILURE); @@ -333,134 +523,86 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, } wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...", - BN_num_bits(data->grp->prime)); - - data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) + - data->id_peer_len); - if (data->outbuf == NULL) { - eap_pwd_state(data, FAILURE); - return; - } - wpabuf_put_be16(data->outbuf, data->group_num); - wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); - wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); - wpabuf_put_data(data->outbuf, id->token, sizeof(id->token)); - wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE); - wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len); - - eap_pwd_state(data, PWD_Commit_Req); -} + (int) crypto_ec_prime_len_bits(data->grp->group)); - -static void -eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, - struct eap_method_ret *ret, - const struct wpabuf *reqData, - const u8 *payload, size_t payload_len) -{ - EC_POINT *K = NULL, *point = NULL; - BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL; - u16 offset; - u8 *ptr, *scalar = NULL, *element = NULL; - size_t prime_len, order_len; - - if (data->state != PWD_Commit_Req) { - ret->ignore = TRUE; - goto fin; - } - - prime_len = BN_num_bytes(data->grp->prime); - order_len = BN_num_bytes(data->grp->order); - - if (payload_len != 2 * prime_len + order_len) { - wpa_printf(MSG_INFO, - "EAP-pwd: Unexpected Commit payload length %u (expected %u)", - (unsigned int) payload_len, - (unsigned int) (2 * prime_len + order_len)); - goto fin; - } - - if (((data->private_value = BN_new()) == NULL) || - ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) || - ((cofactor = BN_new()) == NULL) || - ((data->my_scalar = BN_new()) == NULL) || - ((mask = BN_new()) == NULL)) { + data->private_value = crypto_bignum_init(); + data->my_element = crypto_ec_point_init(data->grp->group); + cofactor = crypto_bignum_init(); + data->my_scalar = crypto_bignum_init(); + mask = crypto_bignum_init(); + if (!data->private_value || !data->my_element || !cofactor || + !data->my_scalar || !mask) { wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail"); goto fin; } - if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) { + if (crypto_ec_cofactor(data->grp->group, cofactor) < 0) { wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor " "for curve"); goto fin; } - if (BN_rand_range(data->private_value, data->grp->order) != 1 || - BN_rand_range(mask, data->grp->order) != 1 || - BN_add(data->my_scalar, data->private_value, mask) != 1 || - BN_mod(data->my_scalar, data->my_scalar, data->grp->order, - data->bnctx) != 1) { + if (crypto_bignum_rand(data->private_value, + crypto_ec_get_order(data->grp->group)) < 0 || + crypto_bignum_rand(mask, + crypto_ec_get_order(data->grp->group)) < 0 || + crypto_bignum_add(data->private_value, mask, + data->my_scalar) < 0 || + crypto_bignum_mod(data->my_scalar, + crypto_ec_get_order(data->grp->group), + data->my_scalar) < 0) { wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get randomness"); goto fin; } - if (!EC_POINT_mul(data->grp->group, data->my_element, NULL, - data->grp->pwe, mask, data->bnctx)) { + if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask, + data->my_element) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation " "fail"); eap_pwd_state(data, FAILURE); goto fin; } - if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx)) - { + if (crypto_ec_point_invert(data->grp->group, data->my_element) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail"); goto fin; } - if (((x = BN_new()) == NULL) || - ((y = BN_new()) == NULL)) { - wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail"); - goto fin; - } - /* process the request */ - if (((data->server_scalar = BN_new()) == NULL) || - ((data->k = BN_new()) == NULL) || - ((K = EC_POINT_new(data->grp->group)) == NULL) || - ((point = EC_POINT_new(data->grp->group)) == NULL) || - ((data->server_element = EC_POINT_new(data->grp->group)) == NULL)) - { + data->k = crypto_bignum_init(); + K = crypto_ec_point_init(data->grp->group); + point = crypto_ec_point_init(data->grp->group); + if (!data->k || !K || !point) { wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation " "fail"); goto fin; } /* element, x then y, followed by scalar */ - ptr = (u8 *) payload; - BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x); - ptr += BN_num_bytes(data->grp->prime); - BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y); - ptr += BN_num_bytes(data->grp->prime); - BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar); - if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group, - data->server_element, x, y, - data->bnctx)) { + data->server_element = crypto_ec_point_from_bin(data->grp->group, ptr); + if (!data->server_element) { wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element " "fail"); goto fin; } + ptr += prime_len * 2; + data->server_scalar = crypto_bignum_init_set(ptr, order_len); + if (!data->server_scalar) { + wpa_printf(MSG_INFO, + "EAP-PWD (peer): setting peer scalar fail"); + goto fin; + } /* check to ensure server's element is not in a small sub-group */ - if (BN_cmp(cofactor, BN_value_one())) { - if (!EC_POINT_mul(data->grp->group, point, NULL, - data->server_element, cofactor, NULL)) { + if (!crypto_bignum_is_one(cofactor)) { + if (crypto_ec_point_mul(data->grp->group, data->server_element, + cofactor, point) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " "server element by order!\n"); goto fin; } - if (EC_POINT_is_at_infinity(data->grp->group, point)) { + if (crypto_ec_point_is_at_infinity(data->grp->group, point)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): server element " "is at infinity!\n"); goto fin; @@ -468,21 +610,20 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, } /* compute the shared key, k */ - if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe, - data->server_scalar, data->bnctx)) || - (!EC_POINT_add(data->grp->group, K, K, data->server_element, - data->bnctx)) || - (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value, - data->bnctx))) { + if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, + data->server_scalar, K) < 0 || + crypto_ec_point_add(data->grp->group, K, data->server_element, + K) < 0 || + crypto_ec_point_mul(data->grp->group, K, data->private_value, + K) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key " "fail"); goto fin; } /* ensure that the shared key isn't in a small sub-group */ - if (BN_cmp(cofactor, BN_value_one())) { - if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor, - NULL)) { + if (!crypto_bignum_is_one(cofactor)) { + if (crypto_ec_point_mul(data->grp->group, K, cofactor, K) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " "shared key point by order"); goto fin; @@ -495,30 +636,22 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, * never going to happen it is a simple and safe check "just to be * sure" so let's be safe. */ - if (EC_POINT_is_at_infinity(data->grp->group, K)) { + if (crypto_ec_point_is_at_infinity(data->grp->group, K)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at " "infinity!\n"); goto fin; } - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k, - NULL, data->bnctx)) { + if (crypto_ec_point_x(data->grp->group, K, data->k) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract " "shared secret from point"); goto fin; } /* now do the response */ - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->my_element, x, y, - data->bnctx)) { - wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail"); - goto fin; - } - - if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) || - ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) == - NULL)) { + scalar = os_zalloc(order_len); + element = os_zalloc(prime_len * 2); + if (!scalar || !element) { wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail"); goto fin; } @@ -528,36 +661,28 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, * sufficiently smaller than the prime or order might need pre-pending * with zeros. */ - os_memset(scalar, 0, BN_num_bytes(data->grp->order)); - os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->my_scalar); - BN_bn2bin(data->my_scalar, scalar + offset); - - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, element + offset); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset); + crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len); + if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element, + element + prime_len) != 0) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail"); + goto fin; + } - data->outbuf = wpabuf_alloc(BN_num_bytes(data->grp->order) + - 2 * BN_num_bytes(data->grp->prime)); + data->outbuf = wpabuf_alloc(order_len + 2 * prime_len); if (data->outbuf == NULL) goto fin; /* we send the element as (x,y) follwed by the scalar */ - wpabuf_put_data(data->outbuf, element, - 2 * BN_num_bytes(data->grp->prime)); - wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order)); + wpabuf_put_data(data->outbuf, element, 2 * prime_len); + wpabuf_put_data(data->outbuf, scalar, order_len); fin: os_free(scalar); os_free(element); - BN_clear_free(x); - BN_clear_free(y); - BN_clear_free(mask); - BN_clear_free(cofactor); - EC_POINT_clear_free(K); - EC_POINT_clear_free(point); + crypto_bignum_deinit(mask, 1); + crypto_bignum_deinit(cofactor, 1); + crypto_ec_point_deinit(K, 1); + crypto_ec_point_deinit(point, 1); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); else @@ -571,12 +696,11 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { - BIGNUM *x = NULL, *y = NULL; - struct crypto_hash *hash; + struct crypto_hash *hash = NULL; u32 cs; u16 grp; u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; - int offset; + size_t prime_len = 0, order_len = 0; if (data->state != PWD_Confirm_Req) { ret->ignore = TRUE; @@ -590,6 +714,9 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; } + prime_len = crypto_ec_prime_len(data->grp->group); + order_len = crypto_ec_order_len(data->grp->group); + /* * first build up the ciphersuite which is group | random_function | * prf @@ -602,9 +729,9 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, ptr += sizeof(u8); *ptr = EAP_PWD_DEFAULT_PRF; - /* each component of the cruft will be at most as big as the prime */ - if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || - ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { + /* each component of the point will be at most as big as the prime */ + cruft = os_malloc(prime_len * 2); + if (!cruft) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm allocation " "fail"); goto fin; @@ -622,65 +749,41 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, * zero the memory each time because this is mod prime math and some * value may start with a few zeros and the previous one did not. */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); - BN_bn2bin(data->k, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len); + eap_pwd_h_update(hash, cruft, prime_len); /* server element: x, y */ - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->server_element, x, y, - data->bnctx)) { + if (crypto_ec_point_to_bin(data->grp->group, data->server_element, + cruft, cruft + prime_len) != 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " "assignment fail"); goto fin; } - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, prime_len * 2); /* server scalar */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->server_scalar); - BN_bn2bin(data->server_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + crypto_bignum_to_bin(data->server_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); /* my element: x, y */ - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->my_element, x, y, - data->bnctx)) { + if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft, + cruft + prime_len) != 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " "assignment fail"); goto fin; } - - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, prime_len * 2); /* my scalar */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->my_scalar); - BN_bn2bin(data->my_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); /* the ciphersuite */ eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); /* random function fin */ eap_pwd_h_final(hash, conf); + hash = NULL; ptr = (u8 *) payload; if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) { @@ -700,66 +803,43 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; /* k */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); - BN_bn2bin(data->k, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len); + eap_pwd_h_update(hash, cruft, prime_len); /* my element */ - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->my_element, x, y, - data->bnctx)) { + if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft, + cruft + prime_len) != 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point " "assignment fail"); goto fin; } - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, prime_len * 2); /* my scalar */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->my_scalar); - BN_bn2bin(data->my_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); /* server element: x, y */ - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->server_element, x, y, - data->bnctx)) { + if (crypto_ec_point_to_bin(data->grp->group, data->server_element, + cruft, cruft + prime_len) != 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point " "assignment fail"); goto fin; } - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, prime_len * 2); /* server scalar */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->server_scalar); - BN_bn2bin(data->server_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + crypto_bignum_to_bin(data->server_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); /* the ciphersuite */ eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); /* all done */ eap_pwd_h_final(hash, conf); + hash = NULL; - if (compute_keys(data->grp, data->bnctx, data->k, + if (compute_keys(data->grp, data->k, data->my_scalar, data->server_scalar, conf, ptr, &cs, data->msk, data->emsk, data->session_id) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | " @@ -774,10 +854,7 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); fin: - if (data->grp) - bin_clear_free(cruft, BN_num_bytes(data->grp->prime)); - BN_clear_free(x); - BN_clear_free(y); + bin_clear_free(cruft, prime_len * 2); if (data->outbuf == NULL) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; @@ -785,6 +862,10 @@ fin: } else { eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION); } + + /* clean allocated memory */ + if (hash) + eap_pwd_h_final(hash, conf); } diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c index 330febbefd78..0a6ce255af4d 100644 --- a/src/eap_peer/eap_sake.c +++ b/src/eap_peer/eap_sake.c @@ -85,12 +85,11 @@ static void * eap_sake_init(struct eap_sm *sm) identity = eap_get_config_identity(sm, &identity_len); if (identity) { - data->peerid = os_malloc(identity_len); + data->peerid = os_memdup(identity, identity_len); if (data->peerid == NULL) { eap_sake_deinit(sm, data); return NULL; } - os_memcpy(data->peerid, identity, identity_len); data->peerid_len = identity_len; } @@ -230,10 +229,9 @@ static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, if (attr.serverid) { wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID", attr.serverid, attr.serverid_len); - data->serverid = os_malloc(attr.serverid_len); + data->serverid = os_memdup(attr.serverid, attr.serverid_len); if (data->serverid == NULL) return NULL; - os_memcpy(data->serverid, attr.serverid, attr.serverid_len); data->serverid_len = attr.serverid_len; } @@ -450,10 +448,9 @@ static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -490,10 +487,9 @@ static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c index b97c95db196f..ba5eea9ddfa2 100644 --- a/src/eap_peer/eap_sim.c +++ b/src/eap_peer/eap_sim.c @@ -46,6 +46,8 @@ struct eap_sim_data { CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE } state; int result_ind, use_result_ind; + int use_pseudonym; + int error_code; }; @@ -93,6 +95,9 @@ static void * eap_sim_init(struct eap_sm *sm) return NULL; } + /* Zero is a valid error code, so we need to initialize */ + data->error_code = NO_EAP_METHOD_ERROR; + data->min_num_chal = 2; if (config && config->phase1) { char *pos = os_strstr(config->phase1, "sim_min_num_chal="); @@ -115,7 +120,8 @@ static void * eap_sim_init(struct eap_sm *sm) NULL; } - if (config && config->anonymous_identity) { + data->use_pseudonym = !sm->init_phase2; + if (config && config->anonymous_identity && data->use_pseudonym) { data->pseudonym = os_malloc(config->anonymous_identity_len); if (data->pseudonym) { os_memcpy(data->pseudonym, config->anonymous_identity, @@ -372,7 +378,8 @@ static void eap_sim_clear_identities(struct eap_sm *sm, os_free(data->pseudonym); data->pseudonym = NULL; data->pseudonym_len = 0; - eap_set_anon_id(sm, NULL, 0); + if (data->use_pseudonym) + eap_set_anon_id(sm, NULL, 0); } if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old reauth_id"); @@ -427,20 +434,21 @@ static int eap_sim_learn_ids(struct eap_sm *sm, struct eap_sim_data *data, realm, realm_len); } data->pseudonym_len = attr->next_pseudonym_len + realm_len; - eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len); + if (data->use_pseudonym) + eap_set_anon_id(sm, data->pseudonym, + data->pseudonym_len); } if (attr->next_reauth_id) { os_free(data->reauth_id); - data->reauth_id = os_malloc(attr->next_reauth_id_len); + data->reauth_id = os_memdup(attr->next_reauth_id, + attr->next_reauth_id_len); if (data->reauth_id == NULL) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " "next reauth_id"); data->reauth_id_len = 0; return -1; } - os_memcpy(data->reauth_id, attr->next_reauth_id, - attr->next_reauth_id_len); data->reauth_id_len = attr->next_reauth_id_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: (encr) AT_NEXT_REAUTH_ID", @@ -635,14 +643,13 @@ static struct wpabuf * eap_sim_process_start(struct eap_sm *sm, } os_free(data->ver_list); - data->ver_list = os_malloc(attr->version_list_len); + data->ver_list = os_memdup(attr->version_list, attr->version_list_len); if (data->ver_list == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to allocate " "memory for version list"); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } - os_memcpy(data->ver_list, attr->version_list, attr->version_list_len); data->ver_list_len = attr->version_list_len; pos = data->ver_list; for (i = 0; i < data->ver_list_len / 2; i++) { @@ -764,8 +771,17 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, } else if (data->pseudonym) { identity = data->pseudonym; identity_len = data->pseudonym_len; - } else - identity = eap_get_config_identity(sm, &identity_len); + } else { + struct eap_peer_config *config; + + config = eap_get_config(sm); + if (config && config->imsi_identity) { + identity = config->imsi_identity; + identity_len = config->imsi_identity_len; + } else { + identity = eap_get_config_identity(sm, &identity_len); + } + } wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Selected identity for MK " "derivation", identity, identity_len); eap_sim_derive_mk(identity, identity_len, data->nonce_mt, @@ -908,6 +924,7 @@ static struct wpabuf * eap_sim_process_notification( eap_sim_report_notification(sm->msg_ctx, attr->notification, 0); if (attr->notification >= 0 && attr->notification < 32768) { + data->error_code = attr->notification; eap_sim_state(data, FAILURE); } else if (attr->notification == EAP_SIM_SUCCESS && data->state == RESULT_SUCCESS) @@ -1181,12 +1198,11 @@ static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_SIM_KEYING_DATA_LEN); + key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN); if (key == NULL) return NULL; *len = EAP_SIM_KEYING_DATA_LEN; - os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); return key; } @@ -1223,17 +1239,33 @@ static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); return key; } +static int eap_sim_get_error_code(void *priv) +{ + struct eap_sim_data *data = priv; + int current_data_error; + + if (!data) + return NO_EAP_METHOD_ERROR; + + current_data_error = data->error_code; + + /* Now reset for next transaction */ + data->error_code = NO_EAP_METHOD_ERROR; + + return current_data_error; +} + + int eap_peer_sim_register(void) { struct eap_method *eap; @@ -1254,6 +1286,7 @@ int eap_peer_sim_register(void) eap->init_for_reauth = eap_sim_init_for_reauth; eap->get_identity = eap_sim_get_identity; eap->get_emsk = eap_sim_get_emsk; + eap->get_error_code = eap_sim_get_error_code; return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c index ca2354f8a785..cb747026cb8a 100644 --- a/src/eap_peer/eap_tls.c +++ b/src/eap_peer/eap_tls.c @@ -173,14 +173,31 @@ static struct wpabuf * eap_tls_failure(struct eap_sm *sm, static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data, struct eap_method_ret *ret) { + const char *label; + wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); - ret->methodState = METHOD_DONE; - ret->decision = DECISION_UNCOND_SUCC; + if (data->ssl.tls_out) { + wpa_printf(MSG_DEBUG, "EAP-TLS: Fragment(s) remaining"); + return; + } + + if (data->ssl.tls_v13) { + label = "EXPORTER_EAP_TLS_Key_Material"; + + /* A possible NewSessionTicket may be received before + * EAP-Success, so need to allow it to be received. */ + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_COND_SUCC; + } else { + label = "client EAP encryption"; + + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + } eap_tls_free_key(data); - data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, - "client EAP encryption", + data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, label, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (data->key_data) { @@ -338,12 +355,11 @@ static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->key_data == NULL) return NULL; - key = os_malloc(EAP_TLS_KEY_LEN); + key = os_memdup(data->key_data, EAP_TLS_KEY_LEN); if (key == NULL) return NULL; *len = EAP_TLS_KEY_LEN; - os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); return key; } @@ -357,12 +373,11 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->key_data == NULL) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; - os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); return key; } @@ -376,12 +391,11 @@ static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (data->session_id == NULL) return NULL; - id = os_malloc(data->id_len); + id = os_memdup(data->session_id, data->id_len); if (id == NULL) return NULL; *len = data->id_len; - os_memcpy(id, data->session_id, data->id_len); return id; } diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index 0dcb9c138f81..0de131526a51 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -80,10 +80,22 @@ static void eap_tls_params_flags(struct tls_connection_params *params, params->flags |= TLS_CONN_DISABLE_TLSv1_2; if (os_strstr(txt, "tls_disable_tlsv1_2=0")) params->flags &= ~TLS_CONN_DISABLE_TLSv1_2; + if (os_strstr(txt, "tls_disable_tlsv1_3=1")) + params->flags |= TLS_CONN_DISABLE_TLSv1_3; + if (os_strstr(txt, "tls_disable_tlsv1_3=0")) + params->flags &= ~TLS_CONN_DISABLE_TLSv1_3; if (os_strstr(txt, "tls_ext_cert_check=1")) params->flags |= TLS_CONN_EXT_CERT_CHECK; if (os_strstr(txt, "tls_ext_cert_check=0")) params->flags &= ~TLS_CONN_EXT_CERT_CHECK; + if (os_strstr(txt, "tls_suiteb=1")) + params->flags |= TLS_CONN_SUITEB; + if (os_strstr(txt, "tls_suiteb=0")) + params->flags &= ~TLS_CONN_SUITEB; + if (os_strstr(txt, "tls_suiteb_no_ecdh=1")) + params->flags |= TLS_CONN_SUITEB_NO_ECDH; + if (os_strstr(txt, "tls_suiteb_no_ecdh=0")) + params->flags &= ~TLS_CONN_SUITEB_NO_ECDH; } @@ -151,6 +163,23 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, */ params->flags |= TLS_CONN_DISABLE_SESSION_TICKET; } + if (data->eap_type == EAP_TYPE_FAST || + data->eap_type == EAP_TYPE_TTLS || + data->eap_type == EAP_TYPE_PEAP) { + /* The current EAP peer implementation is not yet ready for the + * TLS v1.3 changes, so disable this by default for now. */ + params->flags |= TLS_CONN_DISABLE_TLSv1_3; + } + if (data->eap_type == EAP_TYPE_TLS) { + /* While the current EAP-TLS implementation is more or less + * complete for TLS v1.3, there has been no interoperability + * testing with other implementations, so disable for by default + * for now until there has been chance to confirm that no + * significant interoperability issues show up with TLS version + * update. + */ + params->flags |= TLS_CONN_DISABLE_TLSv1_3; + } if (phase2) { wpa_printf(MSG_DEBUG, "TLS: using phase2 config options"); eap_tls_params_from_conf2(params, config); @@ -358,6 +387,13 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, struct tls_random keys; u8 *out; + if (eap_type == EAP_TYPE_TLS && data->tls_v13) { + *len = 64; + return eap_peer_tls_derive_key(sm, data, + "EXPORTER_EAP_TLS_Session-Id", + 64); + } + if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys) || keys.client_random == NULL || keys.server_random == NULL) return NULL; @@ -661,6 +697,8 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, * the AS. */ int res = eap_tls_process_input(sm, data, in_data, out_data); + char buf[20]; + if (res) { /* * Input processing failed (res = -1) or more data is @@ -673,6 +711,12 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, * The incoming message has been reassembled and processed. The * response was allocated into data->tls_out buffer. */ + + if (tls_get_version(data->ssl_ctx, data->conn, + buf, sizeof(buf)) == 0) { + wpa_printf(MSG_DEBUG, "SSL: Using TLS version %s", buf); + data->tls_v13 = os_strcmp(buf, "TLSv1.3") == 0; + } } if (data->tls_out == NULL) { diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h index acd2b783617f..306e6a98bc3f 100644 --- a/src/eap_peer/eap_tls_common.h +++ b/src/eap_peer/eap_tls_common.h @@ -73,6 +73,11 @@ struct eap_ssl_data { * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) */ u8 eap_type; + + /** + * tls_v13 - Whether TLS v1.3 or newer is used + */ + int tls_v13; }; diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c index 92f94dcd6019..f18788ce8cb5 100644 --- a/src/eap_peer/eap_ttls.c +++ b/src/eap_peer/eap_ttls.c @@ -458,7 +458,7 @@ static int eap_ttls_phase2_request_eap(struct eap_sm *sm, if (*resp == NULL && (config->pending_req_identity || config->pending_req_password || - config->pending_req_otp)) { + config->pending_req_otp || config->pending_req_sim)) { return 0; } @@ -624,12 +624,28 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, os_memset(pos, 0, 24); /* LM-Response */ pos += 24; if (pwhash) { - challenge_response(challenge, password, pos); /* NT-Response */ + /* NT-Response */ + if (challenge_response(challenge, password, pos)) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/MSCHAP: Failed derive password hash"); + wpabuf_free(msg); + os_free(challenge); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash", password, 16); } else { - nt_challenge_response(challenge, password, password_len, - pos); /* NT-Response */ + /* NT-Response */ + if (nt_challenge_response(challenge, password, password_len, + pos)) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/MSCHAP: Failed derive password"); + wpabuf_free(msg); + os_free(challenge); + return -1; + } + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password", password, password_len); } @@ -870,13 +886,12 @@ static int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen, { wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); if (parse->eapdata == NULL) { - parse->eapdata = os_malloc(dlen); + parse->eapdata = os_memdup(dpos, dlen); if (parse->eapdata == NULL) { wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate " "memory for Phase 2 EAP data"); return -1; } - os_memcpy(parse->eapdata, dpos, dlen); parse->eap_len = dlen; } else { u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen); @@ -1280,7 +1295,8 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm, } else if (config->pending_req_identity || config->pending_req_password || config->pending_req_otp || - config->pending_req_new_password) { + config->pending_req_new_password || + config->pending_req_sim) { wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_dup(in_decrypted); } @@ -1317,7 +1333,8 @@ static int eap_ttls_implicit_identity_request(struct eap_sm *sm, (config->pending_req_identity || config->pending_req_password || config->pending_req_otp || - config->pending_req_new_password)) { + config->pending_req_new_password || + config->pending_req_sim)) { /* * Use empty buffer to force implicit request * processing when EAP request is re-processed after @@ -1537,7 +1554,7 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, } -static void eap_ttls_check_auth_status(struct eap_sm *sm, +static void eap_ttls_check_auth_status(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret) { @@ -1648,6 +1665,10 @@ static Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv) static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv) { struct eap_ttls_data *data = priv; + + if (data->phase2_priv && data->phase2_method && + data->phase2_method->deinit_for_reauth) + data->phase2_method->deinit_for_reauth(sm, data->phase2_priv); wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = NULL; wpabuf_free(data->pending_resp); @@ -1739,12 +1760,11 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->key_data == NULL || !data->phase2_success) return NULL; - key = os_malloc(EAP_TLS_KEY_LEN); + key = os_memdup(data->key_data, EAP_TLS_KEY_LEN); if (key == NULL) return NULL; *len = EAP_TLS_KEY_LEN; - os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); return key; } @@ -1758,12 +1778,11 @@ static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (data->session_id == NULL || !data->phase2_success) return NULL; - id = os_malloc(data->id_len); + id = os_memdup(data->session_id, data->id_len); if (id == NULL) return NULL; *len = data->id_len; - os_memcpy(id, data->session_id, data->id_len); return id; } @@ -1777,12 +1796,11 @@ static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->key_data == NULL) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; - os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); return key; } diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c index ca6502ea02e7..7bd97b1b997e 100644 --- a/src/eap_peer/ikev2.c +++ b/src/eap_peer/ikev2.c @@ -476,10 +476,9 @@ static int ikev2_process_idi(struct ikev2_responder_data *data, wpa_printf(MSG_DEBUG, "IKEV2: IDi ID Type %d", id_type); wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDi", idi, idi_len); os_free(data->IDi); - data->IDi = os_malloc(idi_len); + data->IDi = os_memdup(idi, idi_len); if (data->IDi == NULL) return -1; - os_memcpy(data->IDi, idi, idi_len); data->IDi_len = idi_len; data->IDi_type = id_type; diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c index 0c5caa7dd522..a9bafe2886c0 100644 --- a/src/eap_peer/tncc.c +++ b/src/eap_peer/tncc.c @@ -126,12 +126,10 @@ static TNC_Result TNC_TNCC_ReportMessageTypes( imc = tnc_imc[imcID]; os_free(imc->supported_types); - imc->supported_types = - os_malloc(typeCount * sizeof(TNC_MessageType)); + imc->supported_types = os_memdup(supportedTypes, + typeCount * sizeof(TNC_MessageType)); if (imc->supported_types == NULL) return TNC_RESULT_FATAL; - os_memcpy(imc->supported_types, supportedTypes, - typeCount * sizeof(TNC_MessageType)); imc->num_supported_types = typeCount; return TNC_RESULT_SUCCESS; diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h index 69eaab8de946..4fbc661c22fe 100644 --- a/src/eap_server/eap.h +++ b/src/eap_server/eap.h @@ -31,6 +31,8 @@ struct eap_user { size_t password_len; int password_hash; /* whether password is hashed with * nt_password_hash() */ + u8 *salt; + size_t salt_len; int phase2; int force_version; unsigned int remediation:1; @@ -38,6 +40,7 @@ struct eap_user { int ttls_auth; /* bitfield of * EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */ struct hostapd_radius_attr *accept_attr; + u32 t_c_timestamp; }; struct eap_eapol_interface { @@ -132,6 +135,7 @@ struct eap_config { size_t server_id_len; int erp; unsigned int tls_session_lifetime; + unsigned int tls_flags; #ifdef CONFIG_TESTING_OPTIONS u32 tls_test_flags; @@ -148,10 +152,12 @@ void eap_sm_notify_cached(struct eap_sm *sm); void eap_sm_pending_cb(struct eap_sm *sm); int eap_sm_method_pending(struct eap_sm *sm); const u8 * eap_get_identity(struct eap_sm *sm, size_t *len); +const char * eap_get_serial_num(struct eap_sm *sm); struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm); void eap_server_clear_identity(struct eap_sm *sm); void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source, const u8 *username, size_t username_len, const u8 *challenge, const u8 *response); +void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len); #endif /* EAP_H */ diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h index c90443d19cb9..cf8a9f0d98e1 100644 --- a/src/eap_server/eap_i.h +++ b/src/eap_server/eap_i.h @@ -159,6 +159,7 @@ struct eap_sm { void *eap_method_priv; u8 *identity; size_t identity_len; + char *serial_num; /* Whether Phase 2 method should validate identity match */ int require_identity_match; int lastId; /* Identifier used in the last EAP-Packet */ @@ -211,6 +212,7 @@ struct eap_sm { Boolean try_initiate_reauth; int erp; unsigned int tls_session_lifetime; + unsigned int tls_flags; #ifdef CONFIG_TESTING_OPTIONS u32 tls_test_flags; diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c index 84ecafc7ca3e..38a1b5c9ee22 100644 --- a/src/eap_server/eap_server.c +++ b/src/eap_server/eap_server.c @@ -326,6 +326,9 @@ SM_STATE(EAP, RETRANSMIT) if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0) sm->eap_if.eapReq = TRUE; } + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_RETRANSMIT MACSTR, + MAC2STR(sm->peer_addr)); } @@ -415,7 +418,7 @@ static void eap_server_erp_init(struct eap_sm *sm) u8 *emsk = NULL; size_t emsk_len = 0; u8 EMSKname[EAP_EMSK_NAME_LEN]; - u8 len[2]; + u8 len[2], ctx[3]; const char *domain; size_t domain_len, nai_buf_len; struct eap_server_erp_key *erp = NULL; @@ -452,7 +455,7 @@ static void eap_server_erp_init(struct eap_sm *sm) wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len); - WPA_PUT_BE16(len, 8); + WPA_PUT_BE16(len, EAP_EMSK_NAME_LEN); if (hmac_sha256_kdf(sm->eap_if.eapSessionId, sm->eap_if.eapSessionIdLen, "EMSK", len, sizeof(len), EMSKname, EAP_EMSK_NAME_LEN) < 0) { @@ -476,9 +479,11 @@ static void eap_server_erp_init(struct eap_sm *sm) erp->rRK_len = emsk_len; wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len); + ctx[0] = EAP_ERP_CS_HMAC_SHA256_128; + WPA_PUT_BE16(&ctx[1], erp->rRK_len); if (hmac_sha256_kdf(erp->rRK, erp->rRK_len, - "EAP Re-authentication Integrity Key@ietf.org", - len, sizeof(len), erp->rIK, erp->rRK_len) < 0) { + "Re-authentication Integrity Key@ietf.org", + ctx, sizeof(ctx), erp->rIK, erp->rRK_len) < 0) { wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP"); goto fail; } @@ -632,6 +637,9 @@ SM_STATE(EAP, TIMEOUT_FAILURE) SM_ENTRY(EAP, TIMEOUT_FAILURE); sm->eap_if.eapTimeout = TRUE; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TIMEOUT_FAILURE MACSTR, + MAC2STR(sm->peer_addr)); } @@ -1009,6 +1017,9 @@ SM_STATE(EAP, RETRANSMIT2) if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0) sm->eap_if.eapReq = TRUE; } + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_RETRANSMIT2 MACSTR, + MAC2STR(sm->peer_addr)); } @@ -1099,6 +1110,9 @@ SM_STATE(EAP, TIMEOUT_FAILURE2) SM_ENTRY(EAP, TIMEOUT_FAILURE2); sm->eap_if.eapTimeout = TRUE; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TIMEOUT_FAILURE2 MACSTR, + MAC2STR(sm->peer_addr)); } @@ -1108,6 +1122,9 @@ SM_STATE(EAP, FAILURE2) eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData); sm->eap_if.eapFail = TRUE; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE2 MACSTR, + MAC2STR(sm->peer_addr)); } @@ -1134,6 +1151,9 @@ SM_STATE(EAP, SUCCESS2) * started properly. */ sm->start_reauth = TRUE; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS2 MACSTR, + MAC2STR(sm->peer_addr)); } @@ -1800,6 +1820,8 @@ static void eap_user_free(struct eap_user *user) return; bin_clear_free(user->password, user->password_len); user->password = NULL; + bin_clear_free(user->salt, user->salt_len); + user->salt = NULL; os_free(user); } @@ -1866,6 +1888,7 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx, sm->server_id_len = conf->server_id_len; sm->erp = conf->erp; sm->tls_session_lifetime = conf->tls_session_lifetime; + sm->tls_flags = conf->tls_flags; #ifdef CONFIG_TESTING_OPTIONS sm->tls_test_flags = conf->tls_test_flags; @@ -1897,6 +1920,7 @@ void eap_server_sm_deinit(struct eap_sm *sm) wpabuf_free(sm->lastReqData); wpabuf_free(sm->eap_if.eapRespData); os_free(sm->identity); + os_free(sm->serial_num); os_free(sm->pac_opaque_encr_key); os_free(sm->eap_fast_a_id); os_free(sm->eap_fast_a_id_info); @@ -1969,6 +1993,55 @@ const u8 * eap_get_identity(struct eap_sm *sm, size_t *len) /** + * eap_get_serial_num - Get the serial number of user certificate + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * Returns: Pointer to the serial number or %NULL if not available + */ +const char * eap_get_serial_num(struct eap_sm *sm) +{ + return sm->serial_num; +} + + +void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len) +{ +#ifdef CONFIG_ERP + const struct eap_hdr *hdr; + const u8 *pos, *end; + struct erp_tlvs parse; + + if (len < sizeof(*hdr) + 1) + return; + hdr = (const struct eap_hdr *) eap; + end = eap + len; + pos = (const u8 *) (hdr + 1); + if (hdr->code != EAP_CODE_INITIATE || *pos != EAP_ERP_TYPE_REAUTH) + return; + pos++; + if (pos + 3 > end) + return; + + /* Skip Flags and SEQ */ + pos += 3; + + if (erp_parse_tlvs(pos, end, &parse, 1) < 0 || !parse.keyname) + return; + wpa_hexdump_ascii(MSG_DEBUG, + "EAP: Update identity based on EAP-Initiate/Re-auth keyName-NAI", + parse.keyname, parse.keyname_len); + os_free(sm->identity); + sm->identity = os_malloc(parse.keyname_len); + if (sm->identity) { + os_memcpy(sm->identity, parse.keyname, parse.keyname_len); + sm->identity_len = parse.keyname_len; + } else { + sm->identity_len = 0; + } +#endif /* CONFIG_ERP */ +} + + +/** * eap_get_interface - Get pointer to EAP-EAPOL interface data * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * Returns: Pointer to the EAP-EAPOL interface data diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c index a8bb5eae6b56..175021163c1d 100644 --- a/src/eap_server/eap_server_aka.c +++ b/src/eap_server/eap_server_aka.c @@ -1261,10 +1261,9 @@ static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_SIM_KEYING_DATA_LEN); + key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); *len = EAP_SIM_KEYING_DATA_LEN; return key; } @@ -1278,10 +1277,9 @@ static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; } diff --git a/src/eap_server/eap_server_eke.c b/src/eap_server/eap_server_eke.c index 1eba8f515648..71580bf7bf52 100644 --- a/src/eap_server/eap_server_eke.c +++ b/src/eap_server/eap_server_eke.c @@ -467,13 +467,12 @@ static void eap_eke_process_identity(struct eap_sm *sm, data->peerid_type = *pos++; os_free(data->peerid); - data->peerid = os_malloc(end - pos); + data->peerid = os_memdup(pos, end - pos); if (data->peerid == NULL) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to allocate memory for peerid"); eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return; } - os_memcpy(data->peerid, pos, end - pos); data->peerid_len = end - pos; wpa_printf(MSG_DEBUG, "EAP-EKE: Peer IDType %u", data->peerid_type); wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Peer Identity", @@ -731,10 +730,9 @@ static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -749,10 +747,9 @@ static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c index 20491726880e..a63f820465c8 100644 --- a/src/eap_server/eap_server_fast.c +++ b/src/eap_server/eap_server_fast.c @@ -471,12 +471,11 @@ static void * eap_fast_init(struct eap_sm *sm) eap_fast_reset(sm, data); return NULL; } - data->srv_id = os_malloc(sm->eap_fast_a_id_len); + data->srv_id = os_memdup(sm->eap_fast_a_id, sm->eap_fast_a_id_len); if (data->srv_id == NULL) { eap_fast_reset(sm, data); return NULL; } - os_memcpy(data->srv_id, sm->eap_fast_a_id, sm->eap_fast_a_id_len); data->srv_id_len = sm->eap_fast_a_id_len; if (sm->eap_fast_a_id_info == NULL) { @@ -561,7 +560,7 @@ static int eap_fast_phase1_done(struct eap_sm *sm, struct eap_fast_data *data) return -1; } data->anon_provisioning = os_strstr(cipher, "ADH") != NULL; - + if (data->anon_provisioning) { wpa_printf(MSG_DEBUG, "EAP-FAST: Anonymous provisioning"); eap_fast_derive_key_provisioning(sm, data); @@ -789,7 +788,7 @@ static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm, /* A-ID (inside PAC-Info) */ eap_fast_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len); - + /* Note: headers may be misaligned after A-ID */ if (sm->identity) { @@ -1517,7 +1516,7 @@ static void eap_fast_process_msg(struct eap_sm *sm, void *priv, if (eap_fast_process_phase1(sm, data)) break; - /* fall through to PHASE2_START */ + /* fall through */ case PHASE2_START: eap_fast_process_phase2_start(sm, data); break; diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c index 94e74ec9b2f7..fb3d11748c8c 100644 --- a/src/eap_server/eap_server_gpsk.c +++ b/src/eap_server/eap_server_gpsk.c @@ -269,13 +269,12 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, return; } os_free(data->id_peer); - data->id_peer = os_malloc(alen); + data->id_peer = os_memdup(pos, alen); if (data->id_peer == NULL) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Not enough memory to store " "%d-octet ID_Peer", alen); return; } - os_memcpy(data->id_peer, pos, alen); data->id_peer_len = alen; wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer", data->id_peer, data->id_peer_len); @@ -575,10 +574,9 @@ static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -593,10 +591,9 @@ static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; @@ -618,10 +615,9 @@ static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - sid = os_malloc(data->id_len); + sid = os_memdup(data->session_id, data->id_len); if (sid == NULL) return NULL; - os_memcpy(sid, data->session_id, data->id_len); *len = data->id_len; return sid; diff --git a/src/eap_server/eap_server_gtc.c b/src/eap_server/eap_server_gtc.c index 193a8517ac08..fcccbcbd5efa 100644 --- a/src/eap_server/eap_server_gtc.c +++ b/src/eap_server/eap_server_gtc.c @@ -141,12 +141,11 @@ static void eap_gtc_process(struct eap_sm *sm, void *priv, } else { os_free(sm->identity); sm->identity_len = pos2 - pos; - sm->identity = os_malloc(sm->identity_len); + sm->identity = os_memdup(pos, sm->identity_len); if (sm->identity == NULL) { data->state = FAILURE; return; } - os_memcpy(sm->identity, pos, sm->identity_len); } if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c index 3a249d141e0c..32e6872045c5 100644 --- a/src/eap_server/eap_server_ikev2.c +++ b/src/eap_server/eap_server_ikev2.c @@ -103,10 +103,9 @@ static void * eap_ikev2_init(struct eap_sm *sm) data->ikev2.proposal.encr = ENCR_AES_CBC; data->ikev2.proposal.dh = DH_GROUP2_1024BIT_MODP; - data->ikev2.IDi = os_malloc(sm->server_id_len); + data->ikev2.IDi = os_memdup(sm->server_id, sm->server_id_len); if (data->ikev2.IDi == NULL) goto failed; - os_memcpy(data->ikev2.IDi, sm->server_id, sm->server_id_len); data->ikev2.IDi_len = sm->server_id_len; data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret; @@ -224,7 +223,7 @@ static struct wpabuf * eap_ikev2_buildReq(struct eap_sm *sm, void *priv, u8 id) } data->out_used = 0; } - /* pass through */ + /* fall through */ case WAIT_FRAG_ACK: return eap_ikev2_build_msg(data, id); case FRAG_ACK: diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c index 460cd9c82ff5..6c47bb636aab 100644 --- a/src/eap_server/eap_server_mschapv2.c +++ b/src/eap_server/eap_server_mschapv2.c @@ -71,13 +71,12 @@ static void * eap_mschapv2_init(struct eap_sm *sm) } if (sm->peer_challenge) { - data->peer_challenge = os_malloc(CHALLENGE_LEN); + data->peer_challenge = os_memdup(sm->peer_challenge, + CHALLENGE_LEN); if (data->peer_challenge == NULL) { os_free(data); return NULL; } - os_memcpy(data->peer_challenge, sm->peer_challenge, - CHALLENGE_LEN); } return data; diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c index 782b8c316537..3257789695cd 100644 --- a/src/eap_server/eap_server_pax.c +++ b/src/eap_server/eap_server_pax.c @@ -327,13 +327,12 @@ static void eap_pax_process_std_2(struct eap_sm *sm, } data->cid_len = cid_len; os_free(data->cid); - data->cid = os_malloc(data->cid_len); + data->cid = os_memdup(pos + 2, data->cid_len); if (data->cid == NULL) { wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for " "CID"); return; } - os_memcpy(data->cid, pos + 2, data->cid_len); pos += 2 + data->cid_len; left -= 2 + data->cid_len; wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", diff --git a/src/eap_server/eap_server_psk.c b/src/eap_server/eap_server_psk.c index 857d421393bc..0eab89339ff6 100644 --- a/src/eap_server/eap_server_psk.c +++ b/src/eap_server/eap_server_psk.c @@ -236,13 +236,12 @@ static void eap_psk_process_2(struct eap_sm *sm, left -= sizeof(*resp); os_free(data->id_p); - data->id_p = os_malloc(left); + data->id_p = os_memdup(cpos, left); if (data->id_p == NULL) { wpa_printf(MSG_INFO, "EAP-PSK: Failed to allocate memory for " "ID_P"); return; } - os_memcpy(data->id_p, cpos, left); data->id_p_len = left; wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PSK: ID_P", data->id_p, data->id_p_len); @@ -371,10 +370,9 @@ static void eap_psk_process_4(struct eap_sm *sm, pos += 16; left -= 16; - decrypted = os_malloc(left); + decrypted = os_memdup(pos, left); if (decrypted == NULL) return; - os_memcpy(decrypted, pos, left); if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), wpabuf_head(respData), 22, decrypted, left, @@ -450,10 +448,9 @@ static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -468,10 +465,9 @@ static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c index 64bf708e039a..d0fa54a3aba7 100644 --- a/src/eap_server/eap_server_pwd.c +++ b/src/eap_server/eap_server_pwd.c @@ -11,6 +11,7 @@ #include "common.h" #include "crypto/sha256.h" #include "crypto/ms_funcs.h" +#include "crypto/crypto.h" #include "eap_server/eap_i.h" #include "eap_common/eap_pwd_common.h" @@ -26,8 +27,11 @@ struct eap_pwd_data { u8 *password; size_t password_len; int password_hash; + u8 *salt; + size_t salt_len; u32 token; u16 group_num; + u8 password_prep; EAP_PWD_group *grp; struct wpabuf *inbuf; @@ -36,20 +40,18 @@ struct eap_pwd_data { size_t out_frag_pos; size_t mtu; - BIGNUM *k; - BIGNUM *private_value; - BIGNUM *peer_scalar; - BIGNUM *my_scalar; - EC_POINT *my_element; - EC_POINT *peer_element; + struct crypto_bignum *k; + struct crypto_bignum *private_value; + struct crypto_bignum *peer_scalar; + struct crypto_bignum *my_scalar; + struct crypto_ec_point *my_element; + struct crypto_ec_point *peer_element; u8 my_confirm[SHA256_MAC_LEN]; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; u8 session_id[1 + SHA256_MAC_LEN]; - - BN_CTX *bnctx; }; @@ -116,13 +118,17 @@ static void * eap_pwd_init(struct eap_sm *sm) os_memcpy(data->password, sm->user->password, data->password_len); data->password_hash = sm->user->password_hash; - data->bnctx = BN_CTX_new(); - if (data->bnctx == NULL) { - wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail"); - bin_clear_free(data->password, data->password_len); - bin_clear_free(data->id_server, data->id_server_len); - os_free(data); - return NULL; + data->salt_len = sm->user->salt_len; + if (data->salt_len) { + data->salt = os_memdup(sm->user->salt, sm->user->salt_len); + if (!data->salt) { + wpa_printf(MSG_INFO, + "EAP-pwd: Memory allocation of salt failed"); + bin_clear_free(data->id_server, data->id_server_len); + bin_clear_free(data->password, data->password_len); + os_free(data); + return NULL; + } } data->in_frag_pos = data->out_frag_pos = 0; @@ -138,21 +144,19 @@ static void eap_pwd_reset(struct eap_sm *sm, void *priv) { struct eap_pwd_data *data = priv; - BN_clear_free(data->private_value); - BN_clear_free(data->peer_scalar); - BN_clear_free(data->my_scalar); - BN_clear_free(data->k); - BN_CTX_free(data->bnctx); - EC_POINT_clear_free(data->my_element); - EC_POINT_clear_free(data->peer_element); + crypto_bignum_deinit(data->private_value, 1); + crypto_bignum_deinit(data->peer_scalar, 1); + crypto_bignum_deinit(data->my_scalar, 1); + crypto_bignum_deinit(data->k, 1); + crypto_ec_point_deinit(data->my_element, 1); + crypto_ec_point_deinit(data->peer_element, 1); bin_clear_free(data->id_peer, data->id_peer_len); bin_clear_free(data->id_server, data->id_server_len); bin_clear_free(data->password, data->password_len); + bin_clear_free(data->salt, data->salt_len); if (data->grp) { - EC_GROUP_free(data->grp->group); - EC_POINT_clear_free(data->grp->pwe); - BN_clear_free(data->grp->order); - BN_clear_free(data->grp->prime); + crypto_ec_deinit(data->grp->group); + crypto_ec_point_deinit(data->grp->pwe, 1); os_free(data->grp); } wpabuf_free(data->inbuf); @@ -185,12 +189,45 @@ static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, return; } + wpa_hexdump_key(MSG_DEBUG, "EAP-pwd (server): password", + data->password, data->password_len); + if (data->salt_len) + wpa_hexdump(MSG_DEBUG, "EAP-pwd (server): salt", + data->salt, data->salt_len); + + /* + * If this is a salted password then figure out how it was hashed + * based on the length. + */ + if (data->salt_len) { + switch (data->password_len) { + case 20: + data->password_prep = EAP_PWD_PREP_SSHA1; + break; + case 32: + data->password_prep = EAP_PWD_PREP_SSHA256; + break; + case 64: + data->password_prep = EAP_PWD_PREP_SSHA512; + break; + default: + wpa_printf(MSG_INFO, + "EAP-pwd (server): bad size %d for salted password", + (int) data->password_len); + eap_pwd_state(data, FAILURE); + return; + } + } else { + /* Otherwise, figure out whether it's MS hashed or plain */ + data->password_prep = data->password_hash ? EAP_PWD_PREP_MS : + EAP_PWD_PREP_NONE; + } + wpabuf_put_be16(data->outbuf, data->group_num); wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); wpabuf_put_data(data->outbuf, &data->token, sizeof(data->token)); - wpabuf_put_u8(data->outbuf, data->password_hash ? EAP_PWD_PREP_MS : - EAP_PWD_PREP_NONE); + wpabuf_put_u8(data->outbuf, data->password_prep); wpabuf_put_data(data->outbuf, data->id_server, data->id_server_len); } @@ -198,9 +235,9 @@ static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, static void eap_pwd_build_commit_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) { - BIGNUM *mask = NULL, *x = NULL, *y = NULL; + struct crypto_bignum *mask = NULL; u8 *scalar = NULL, *element = NULL; - u16 offset; + size_t prime_len, order_len; wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request"); /* @@ -210,93 +247,82 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, if (data->out_frag_pos) return; - if (((data->private_value = BN_new()) == NULL) || - ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) || - ((data->my_scalar = BN_new()) == NULL) || - ((mask = BN_new()) == NULL)) { + prime_len = crypto_ec_prime_len(data->grp->group); + order_len = crypto_ec_order_len(data->grp->group); + + data->private_value = crypto_bignum_init(); + data->my_element = crypto_ec_point_init(data->grp->group); + data->my_scalar = crypto_bignum_init(); + mask = crypto_bignum_init(); + if (!data->private_value || !data->my_element || !data->my_scalar || + !mask) { wpa_printf(MSG_INFO, "EAP-PWD (server): scalar allocation " "fail"); goto fin; } - if (BN_rand_range(data->private_value, data->grp->order) != 1 || - BN_rand_range(mask, data->grp->order) != 1 || - BN_add(data->my_scalar, data->private_value, mask) != 1 || - BN_mod(data->my_scalar, data->my_scalar, data->grp->order, - data->bnctx) != 1) { + if (crypto_bignum_rand(data->private_value, + crypto_ec_get_order(data->grp->group)) < 0 || + crypto_bignum_rand(mask, + crypto_ec_get_order(data->grp->group)) < 0 || + crypto_bignum_add(data->private_value, mask, data->my_scalar) < 0 || + crypto_bignum_mod(data->my_scalar, + crypto_ec_get_order(data->grp->group), + data->my_scalar) < 0) { wpa_printf(MSG_INFO, "EAP-pwd (server): unable to get randomness"); goto fin; } - if (!EC_POINT_mul(data->grp->group, data->my_element, NULL, - data->grp->pwe, mask, data->bnctx)) { + if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask, + data->my_element) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): element allocation " "fail"); eap_pwd_state(data, FAILURE); goto fin; } - if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx)) - { + if (crypto_ec_point_invert(data->grp->group, data->my_element) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): element inversion " "fail"); goto fin; } - BN_clear_free(mask); - if (((x = BN_new()) == NULL) || - ((y = BN_new()) == NULL)) { - wpa_printf(MSG_INFO, "EAP-PWD (server): point allocation " - "fail"); + scalar = os_malloc(order_len); + element = os_malloc(prime_len * 2); + if (!scalar || !element) { + wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail"); goto fin; } - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->my_element, x, y, - data->bnctx)) { + + if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element, + element + prime_len) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment " "fail"); goto fin; } - if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) || - ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) == - NULL)) { - wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail"); - goto fin; - } - - /* - * bignums occupy as little memory as possible so one that is - * sufficiently smaller than the prime or order might need pre-pending - * with zeros. - */ - os_memset(scalar, 0, BN_num_bytes(data->grp->order)); - os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->my_scalar); - BN_bn2bin(data->my_scalar, scalar + offset); - - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, element + offset); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset); + crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len); - data->outbuf = wpabuf_alloc(2 * BN_num_bytes(data->grp->prime) + - BN_num_bytes(data->grp->order)); + data->outbuf = wpabuf_alloc(2 * prime_len + order_len + + (data->salt ? 1 + data->salt_len : 0)); if (data->outbuf == NULL) goto fin; + /* If we're doing salted password prep, add the salt */ + if (data->salt_len) { + wpabuf_put_u8(data->outbuf, data->salt_len); + wpabuf_put_data(data->outbuf, data->salt, data->salt_len); + } + /* We send the element as (x,y) followed by the scalar */ - wpabuf_put_data(data->outbuf, element, - 2 * BN_num_bytes(data->grp->prime)); - wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order)); + wpabuf_put_data(data->outbuf, element, 2 * prime_len); + wpabuf_put_data(data->outbuf, scalar, order_len); fin: + crypto_bignum_deinit(mask, 1); os_free(scalar); os_free(element); - BN_clear_free(x); - BN_clear_free(y); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); } @@ -305,11 +331,10 @@ fin: static void eap_pwd_build_confirm_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) { - BIGNUM *x = NULL, *y = NULL; struct crypto_hash *hash; u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; u16 grp; - int offset; + size_t prime_len, order_len; wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request"); /* @@ -319,9 +344,12 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm, if (data->out_frag_pos) return; + prime_len = crypto_ec_prime_len(data->grp->group); + order_len = crypto_ec_order_len(data->grp->group); + /* Each component of the cruft will be at most as big as the prime */ - if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || - ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { + cruft = os_malloc(prime_len * 2); + if (!cruft) { wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation " "fail"); goto fin; @@ -341,64 +369,38 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm, * * First is k */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); - BN_bn2bin(data->k, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len); + eap_pwd_h_update(hash, cruft, prime_len); /* server element: x, y */ - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->my_element, x, y, - data->bnctx)) { + if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft, + cruft + prime_len) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " "assignment fail"); goto fin; } - - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, prime_len * 2); /* server scalar */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->my_scalar); - BN_bn2bin(data->my_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); /* peer element: x, y */ - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->peer_element, x, y, - data->bnctx)) { + if (crypto_ec_point_to_bin(data->grp->group, data->peer_element, cruft, + cruft + prime_len) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " "assignment fail"); goto fin; } - - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, prime_len * 2); /* peer scalar */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->peer_scalar); - BN_bn2bin(data->peer_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + crypto_bignum_to_bin(data->peer_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); /* ciphersuite */ grp = htons(data->group_num); - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + os_memset(cruft, 0, prime_len); ptr = cruft; os_memcpy(ptr, &grp, sizeof(u16)); ptr += sizeof(u16); @@ -419,9 +421,7 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm, wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); fin: - bin_clear_free(cruft, BN_num_bytes(data->grp->prime)); - BN_clear_free(x); - BN_clear_free(y); + bin_clear_free(cruft, prime_len * 2); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); } @@ -602,11 +602,16 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm, if ((data->group_num != be_to_host16(id->group_num)) || (id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) || (os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) || - (id->prf != EAP_PWD_DEFAULT_PRF)) { + (id->prf != EAP_PWD_DEFAULT_PRF) || + (id->prep != data->password_prep)) { wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters"); eap_pwd_state(data, FAILURE); return; } + if (data->id_peer || data->grp) { + wpa_printf(MSG_INFO, "EAP-pwd: data was already allocated"); + return; + } data->id_peer = os_malloc(payload_len - sizeof(struct eap_pwd_id)); if (data->id_peer == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); @@ -617,14 +622,19 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm, wpa_hexdump_ascii(MSG_DEBUG, "EAP-PWD (server): peer sent id of", data->id_peer, data->id_peer_len); - data->grp = os_zalloc(sizeof(EAP_PWD_group)); + data->grp = get_eap_pwd_group(data->group_num); if (data->grp == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for " "group"); return; } - if (data->password_hash) { + /* + * If it's PREP_MS then hash the password again, otherwise regardless + * of the prep the client is doing, the password we have is the one to + * use to generate the password element. + */ + if (data->password_prep == EAP_PWD_PREP_MS) { res = hash_nt_password_hash(data->password, pwhashhash); if (res) return; @@ -647,7 +657,7 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm, return; } wpa_printf(MSG_DEBUG, "EAP-PWD (server): computed %d bit PWE...", - BN_num_bits(data->grp->prime)); + (int) crypto_ec_prime_len_bits(data->grp->group)); eap_pwd_state(data, PWD_Commit_Req); } @@ -657,16 +667,16 @@ static void eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { - u8 *ptr; - BIGNUM *x = NULL, *y = NULL, *cofactor = NULL; - EC_POINT *K = NULL, *point = NULL; + const u8 *ptr; + struct crypto_bignum *cofactor = NULL; + struct crypto_ec_point *K = NULL, *point = NULL; int res = 0; size_t prime_len, order_len; wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response"); - prime_len = BN_num_bytes(data->grp->prime); - order_len = BN_num_bytes(data->grp->order); + prime_len = crypto_ec_prime_len(data->grp->group); + order_len = crypto_ec_order_len(data->grp->group); if (payload_len != 2 * prime_len + order_len) { wpa_printf(MSG_INFO, @@ -676,49 +686,47 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; } - if (((data->peer_scalar = BN_new()) == NULL) || - ((data->k = BN_new()) == NULL) || - ((cofactor = BN_new()) == NULL) || - ((x = BN_new()) == NULL) || - ((y = BN_new()) == NULL) || - ((point = EC_POINT_new(data->grp->group)) == NULL) || - ((K = EC_POINT_new(data->grp->group)) == NULL) || - ((data->peer_element = EC_POINT_new(data->grp->group)) == NULL)) { + data->k = crypto_bignum_init(); + cofactor = crypto_bignum_init(); + point = crypto_ec_point_init(data->grp->group); + K = crypto_ec_point_init(data->grp->group); + if (!data->k || !cofactor || !point || !K) { wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation " "fail"); goto fin; } - if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) { + if (crypto_ec_cofactor(data->grp->group, cofactor) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get " "cofactor for curve"); goto fin; } /* element, x then y, followed by scalar */ - ptr = (u8 *) payload; - BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x); - ptr += BN_num_bytes(data->grp->prime); - BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y); - ptr += BN_num_bytes(data->grp->prime); - BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->peer_scalar); - if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group, - data->peer_element, x, y, - data->bnctx)) { + ptr = payload; + data->peer_element = crypto_ec_point_from_bin(data->grp->group, ptr); + if (!data->peer_element) { wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element " "fail"); goto fin; } + ptr += prime_len * 2; + data->peer_scalar = crypto_bignum_init_set(ptr, order_len); + if (!data->peer_scalar) { + wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation " + "fail"); + goto fin; + } /* check to ensure peer's element is not in a small sub-group */ - if (BN_cmp(cofactor, BN_value_one())) { - if (!EC_POINT_mul(data->grp->group, point, NULL, - data->peer_element, cofactor, NULL)) { + if (!crypto_bignum_is_one(cofactor)) { + if (crypto_ec_point_mul(data->grp->group, data->peer_element, + cofactor, point) != 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): cannot " "multiply peer element by order"); goto fin; } - if (EC_POINT_is_at_infinity(data->grp->group, point)) { + if (crypto_ec_point_is_at_infinity(data->grp->group, point)) { wpa_printf(MSG_INFO, "EAP-PWD (server): peer element " "is at infinity!\n"); goto fin; @@ -726,21 +734,21 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, } /* compute the shared key, k */ - if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe, - data->peer_scalar, data->bnctx)) || - (!EC_POINT_add(data->grp->group, K, K, data->peer_element, - data->bnctx)) || - (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value, - data->bnctx))) { + if ((crypto_ec_point_mul(data->grp->group, data->grp->pwe, + data->peer_scalar, K) < 0) || + (crypto_ec_point_add(data->grp->group, K, data->peer_element, + K) < 0) || + (crypto_ec_point_mul(data->grp->group, K, data->private_value, + K) < 0)) { wpa_printf(MSG_INFO, "EAP-PWD (server): computing shared key " "fail"); goto fin; } /* ensure that the shared key isn't in a small sub-group */ - if (BN_cmp(cofactor, BN_value_one())) { - if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor, - NULL)) { + if (!crypto_bignum_is_one(cofactor)) { + if (crypto_ec_point_mul(data->grp->group, K, cofactor, + K) != 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): cannot " "multiply shared key point by order!\n"); goto fin; @@ -753,13 +761,12 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, * never going to happen it is a simple and safe check "just to be * sure" so let's be safe. */ - if (EC_POINT_is_at_infinity(data->grp->group, K)) { + if (crypto_ec_point_is_at_infinity(data->grp->group, K)) { wpa_printf(MSG_INFO, "EAP-PWD (server): shared key point is " "at infinity"); goto fin; } - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k, - NULL, data->bnctx)) { + if (crypto_ec_point_x(data->grp->group, K, data->k)) { wpa_printf(MSG_INFO, "EAP-PWD (server): unable to extract " "shared secret from secret point"); goto fin; @@ -767,11 +774,9 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, res = 1; fin: - EC_POINT_clear_free(K); - EC_POINT_clear_free(point); - BN_clear_free(cofactor); - BN_clear_free(x); - BN_clear_free(y); + crypto_ec_point_deinit(K, 1); + crypto_ec_point_deinit(point, 1); + crypto_bignum_deinit(cofactor, 1); if (res) eap_pwd_state(data, PWD_Confirm_Req); @@ -784,12 +789,14 @@ static void eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { - BIGNUM *x = NULL, *y = NULL; struct crypto_hash *hash; u32 cs; u16 grp; u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; - int offset; + size_t prime_len, order_len; + + prime_len = crypto_ec_prime_len(data->grp->group); + order_len = crypto_ec_order_len(data->grp->group); if (payload_len != SHA256_MAC_LEN) { wpa_printf(MSG_INFO, @@ -808,8 +815,8 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, *ptr = EAP_PWD_DEFAULT_PRF; /* each component of the cruft will be at most as big as the prime */ - if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || - ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { + cruft = os_malloc(prime_len * 2); + if (!cruft) { wpa_printf(MSG_INFO, "EAP-PWD (peer): allocation fail"); goto fin; } @@ -823,62 +830,36 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; /* k */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); - BN_bn2bin(data->k, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len); + eap_pwd_h_update(hash, cruft, prime_len); /* peer element: x, y */ - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->peer_element, x, y, - data->bnctx)) { + if (crypto_ec_point_to_bin(data->grp->group, data->peer_element, cruft, + cruft + prime_len) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " "assignment fail"); goto fin; } - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, prime_len * 2); /* peer scalar */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->peer_scalar); - BN_bn2bin(data->peer_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + crypto_bignum_to_bin(data->peer_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); /* server element: x, y */ - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->my_element, x, y, - data->bnctx)) { + if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft, + cruft + prime_len) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " "assignment fail"); goto fin; } - - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, prime_len * 2); /* server scalar */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->my_scalar); - BN_bn2bin(data->my_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); /* ciphersuite */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); /* all done */ @@ -892,7 +873,7 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, } wpa_printf(MSG_DEBUG, "EAP-pwd (server): confirm verified"); - if (compute_keys(data->grp, data->bnctx, data->k, + if (compute_keys(data->grp, data->k, data->peer_scalar, data->my_scalar, conf, data->my_confirm, &cs, data->msk, data->emsk, data->session_id) < 0) @@ -901,9 +882,7 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, eap_pwd_state(data, SUCCESS); fin: - bin_clear_free(cruft, BN_num_bytes(data->grp->prime)); - BN_clear_free(x); - BN_clear_free(y); + bin_clear_free(cruft, prime_len * 2); } @@ -1033,11 +1012,10 @@ static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -1052,11 +1030,10 @@ static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; @@ -1085,11 +1062,10 @@ static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - id = os_malloc(1 + SHA256_MAC_LEN); + id = os_memdup(data->session_id, 1 + SHA256_MAC_LEN); if (id == NULL) return NULL; - os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN); *len = 1 + SHA256_MAC_LEN; return id; @@ -1127,4 +1103,3 @@ int eap_server_pwd_register(void) return eap_server_method_register(eap); } - diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c index 84d0e0be4dd1..66183f5f5c04 100644 --- a/src/eap_server/eap_server_sake.c +++ b/src/eap_server/eap_server_sake.c @@ -326,10 +326,9 @@ static void eap_sake_process_challenge(struct eap_sm *sm, data->peerid = NULL; data->peerid_len = 0; if (attr.peerid) { - data->peerid = os_malloc(attr.peerid_len); + data->peerid = os_memdup(attr.peerid, attr.peerid_len); if (data->peerid == NULL) return; - os_memcpy(data->peerid, attr.peerid, attr.peerid_len); data->peerid_len = attr.peerid_len; } @@ -460,10 +459,9 @@ static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -478,10 +476,9 @@ static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c index 3a6ed795c768..10637d4c66b9 100644 --- a/src/eap_server/eap_server_sim.c +++ b/src/eap_server/eap_server_sim.c @@ -787,10 +787,9 @@ static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_SIM_KEYING_DATA_LEN); + key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); *len = EAP_SIM_KEYING_DATA_LEN; return key; } @@ -804,10 +803,9 @@ static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; } diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c index 7249858844ef..8b9e53c61d79 100644 --- a/src/eap_server/eap_server_tls.c +++ b/src/eap_server/eap_server_tls.c @@ -302,17 +302,22 @@ static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_tls_data *data = priv; u8 *eapKeyData; + const char *label; if (data->state != SUCCESS) return NULL; - eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "client EAP encryption", - EAP_TLS_KEY_LEN); + if (data->ssl.tls_v13) + label = "EXPORTER_EAP_TLS_Key_Material"; + else + label = "client EAP encryption"; + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label, + EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { *len = EAP_TLS_KEY_LEN; wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key", eapKeyData, EAP_TLS_KEY_LEN); + os_memset(eapKeyData + EAP_TLS_KEY_LEN, 0, EAP_EMSK_LEN); } else { wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key"); } @@ -325,12 +330,16 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_tls_data *data = priv; u8 *eapKeyData, *emsk; + const char *label; if (data->state != SUCCESS) return NULL; - eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "client EAP encryption", + if (data->ssl.tls_v13) + label = "EXPORTER_EAP_TLS_Key_Material"; + else + label = "client EAP encryption"; + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { emsk = os_malloc(EAP_EMSK_LEN); diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c index 69096954b826..0ae7867fccf7 100644 --- a/src/eap_server/eap_server_tls_common.c +++ b/src/eap_server/eap_server_tls_common.c @@ -47,7 +47,7 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, int verify_peer, int eap_type) { u8 session_ctx[8]; - unsigned int flags = 0; + unsigned int flags = sm->tls_flags; if (sm->ssl_ctx == NULL) { wpa_printf(MSG_ERROR, "TLS context not initialized - cannot use TLS-based EAP method"); @@ -107,7 +107,7 @@ void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, - char *label, size_t len) + const char *label, size_t len) { u8 *out; @@ -145,6 +145,13 @@ u8 * eap_server_tls_derive_session_id(struct eap_sm *sm, struct tls_random keys; u8 *out; + if (eap_type == EAP_TYPE_TLS && data->tls_v13) { + *len = 64; + return eap_server_tls_derive_key(sm, data, + "EXPORTER_EAP_TLS_Session-Id", + 64); + } + if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys)) return NULL; @@ -305,6 +312,8 @@ static int eap_server_tls_process_fragment(struct eap_ssl_data *data, int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data) { + char buf[20]; + if (data->tls_out) { /* This should not happen.. */ wpa_printf(MSG_INFO, "SSL: pending tls_out data when " @@ -327,6 +336,16 @@ int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data) return -1; } + if (tls_get_version(sm->ssl_ctx, data->conn, buf, sizeof(buf)) == 0) { + wpa_printf(MSG_DEBUG, "SSL: Using TLS version %s", buf); + data->tls_v13 = os_strcmp(buf, "TLSv1.3") == 0; + } + + if (!sm->serial_num && + tls_connection_established(sm->ssl_ctx, data->conn)) + sm->serial_num = tls_connection_peer_serial_num(sm->ssl_ctx, + data->conn); + return 0; } @@ -373,7 +392,7 @@ static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags, if (data->tls_in && eap_server_tls_process_cont(data, *pos, end - *pos) < 0) return -1; - + if (flags & EAP_TLS_FLAGS_MORE_FRAGMENTS) { if (eap_server_tls_process_fragment(data, flags, tls_msg_len, *pos, end - *pos) < 0) diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c index a53633f8f1fe..b14996b0b990 100644 --- a/src/eap_server/eap_server_ttls.c +++ b/src/eap_server/eap_server_ttls.c @@ -228,14 +228,13 @@ static int eap_ttls_avp_parse(struct wpabuf *buf, struct eap_ttls_avp *parse) if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) { wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); if (parse->eap == NULL) { - parse->eap = os_malloc(dlen); + parse->eap = os_memdup(dpos, dlen); if (parse->eap == NULL) { wpa_printf(MSG_WARNING, "EAP-TTLS: " "failed to allocate memory " "for Phase 2 EAP data"); goto fail; } - os_memcpy(parse->eap, dpos, dlen); parse->eap_len = dlen; } else { u8 *neweap = os_realloc(parse->eap, @@ -372,7 +371,7 @@ static void eap_ttls_reset(struct eap_sm *sm, void *priv) static struct wpabuf * eap_ttls_build_start(struct eap_sm *sm, struct eap_ttls_data *data, u8 id) -{ +{ struct wpabuf *req; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, 1, @@ -666,11 +665,14 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, } os_free(chal); - if (sm->user->password_hash) - challenge_response(challenge, sm->user->password, nt_response); - else - nt_challenge_response(challenge, sm->user->password, - sm->user->password_len, nt_response); + if ((sm->user->password_hash && + challenge_response(challenge, sm->user->password, nt_response)) || + (!sm->user->password_hash && + nt_challenge_response(challenge, sm->user->password, + sm->user->password_len, nt_response))) { + eap_ttls_state(data, FAILURE); + return; + } if (os_memcmp_const(nt_response, response + 2 + 24, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response"); @@ -1051,12 +1053,11 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, } os_free(sm->identity); - sm->identity = os_malloc(parse.user_name_len); + sm->identity = os_memdup(parse.user_name, parse.user_name_len); if (sm->identity == NULL) { eap_ttls_state(data, FAILURE); goto done; } - os_memcpy(sm->identity, parse.user_name, parse.user_name_len); sm->identity_len = parse.user_name_len; if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1) != 0) { diff --git a/src/eap_server/eap_server_wsc.c b/src/eap_server/eap_server_wsc.c index 7d9d285c39d0..4a5cb980afac 100644 --- a/src/eap_server/eap_server_wsc.c +++ b/src/eap_server/eap_server_wsc.c @@ -257,7 +257,7 @@ static struct wpabuf * eap_wsc_buildReq(struct eap_sm *sm, void *priv, u8 id) } data->out_used = 0; } - /* pass through */ + /* fall through */ case WAIT_FRAG_ACK: return eap_wsc_build_msg(data, id); case FRAG_ACK: diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h index dc943eb207d7..31f6e72d779a 100644 --- a/src/eap_server/eap_tls_common.h +++ b/src/eap_server/eap_tls_common.h @@ -50,6 +50,11 @@ struct eap_ssl_data { enum { MSG, FRAG_ACK, WAIT_FRAG_ACK } state; struct wpabuf tmpbuf; + + /** + * tls_v13 - Whether TLS v1.3 or newer is used + */ + int tls_v13; }; @@ -73,7 +78,7 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, int verify_peer, int eap_type); void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, - char *label, size_t len); + const char *label, size_t len); u8 * eap_server_tls_derive_session_id(struct eap_sm *sm, struct eap_ssl_data *data, u8 eap_type, size_t *len); diff --git a/src/eap_server/ikev2.c b/src/eap_server/ikev2.c index 5385cd89246f..0e9210e67b50 100644 --- a/src/eap_server/ikev2.c +++ b/src/eap_server/ikev2.c @@ -544,10 +544,9 @@ static int ikev2_process_idr(struct ikev2_initiator_data *data, } os_free(data->IDr); } - data->IDr = os_malloc(idr_len); + data->IDr = os_memdup(idr, idr_len); if (data->IDr == NULL) return -1; - os_memcpy(data->IDr, idr, idr_len); data->IDr_len = idr_len; data->IDr_type = id_type; @@ -1147,10 +1146,9 @@ static struct wpabuf * ikev2_build_sa_auth(struct ikev2_initiator_data *data) return NULL; } else { os_free(data->shared_secret); - data->shared_secret = os_malloc(secret_len); + data->shared_secret = os_memdup(secret, secret_len); if (data->shared_secret == NULL) return NULL; - os_memcpy(data->shared_secret, secret, secret_len); data->shared_secret_len = secret_len; } diff --git a/src/eap_server/tncs.c b/src/eap_server/tncs.c index cfcbd3ed828c..942a195761ac 100644 --- a/src/eap_server/tncs.c +++ b/src/eap_server/tncs.c @@ -161,12 +161,10 @@ static TNC_Result TNC_TNCS_ReportMessageTypes( if (imv == NULL) return TNC_RESULT_INVALID_PARAMETER; os_free(imv->supported_types); - imv->supported_types = - os_malloc(typeCount * sizeof(TNC_MessageType)); + imv->supported_types = os_memdup(supportedTypes, + typeCount * sizeof(TNC_MessageType)); if (imv->supported_types == NULL) return TNC_RESULT_FATAL; - os_memcpy(imv->supported_types, supportedTypes, - typeCount * sizeof(TNC_MessageType)); imv->num_supported_types = typeCount; return TNC_RESULT_SUCCESS; diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c index ff673bb2e785..36074d3e0474 100644 --- a/src/eapol_auth/eapol_auth_sm.c +++ b/src/eapol_auth/eapol_auth_sm.c @@ -848,6 +848,7 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, eap_conf.server_id_len = eapol->conf.server_id_len; eap_conf.erp = eapol->conf.erp; eap_conf.tls_session_lifetime = eapol->conf.tls_session_lifetime; + eap_conf.tls_flags = eapol->conf.tls_flags; sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); if (sm->eap == NULL) { eapol_auth_free(sm); @@ -1197,30 +1198,27 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, dst->server_id = src->server_id; dst->server_id_len = src->server_id_len; if (src->eap_req_id_text) { - dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len); + dst->eap_req_id_text = os_memdup(src->eap_req_id_text, + src->eap_req_id_text_len); if (dst->eap_req_id_text == NULL) return -1; - os_memcpy(dst->eap_req_id_text, src->eap_req_id_text, - src->eap_req_id_text_len); dst->eap_req_id_text_len = src->eap_req_id_text_len; } else { dst->eap_req_id_text = NULL; dst->eap_req_id_text_len = 0; } if (src->pac_opaque_encr_key) { - dst->pac_opaque_encr_key = os_malloc(16); + dst->pac_opaque_encr_key = os_memdup(src->pac_opaque_encr_key, + 16); if (dst->pac_opaque_encr_key == NULL) goto fail; - os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key, - 16); } else dst->pac_opaque_encr_key = NULL; if (src->eap_fast_a_id) { - dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len); + dst->eap_fast_a_id = os_memdup(src->eap_fast_a_id, + src->eap_fast_a_id_len); if (dst->eap_fast_a_id == NULL) goto fail; - os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id, - src->eap_fast_a_id_len); dst->eap_fast_a_id_len = src->eap_fast_a_id_len; } else dst->eap_fast_a_id = NULL; @@ -1249,6 +1247,7 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, dst->erp_send_reauth_start = src->erp_send_reauth_start; dst->erp = src->erp; dst->tls_session_lifetime = src->tls_session_lifetime; + dst->tls_flags = src->tls_flags; return 0; diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h index e1974e4354da..44f3f31cc601 100644 --- a/src/eapol_auth/eapol_auth_sm.h +++ b/src/eapol_auth/eapol_auth_sm.h @@ -28,6 +28,7 @@ struct eapol_auth_config { char *erp_domain; /* a copy of this will be allocated */ int erp; /* Whether ERP is enabled on authentication server */ unsigned int tls_session_lifetime; + unsigned int tls_flags; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index 65460fc3bec0..9f029b0d3710 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -16,6 +16,7 @@ #include "crypto/md5.h" #include "common/eapol_common.h" #include "eap_peer/eap.h" +#include "eap_peer/eap_config.h" #include "eap_peer/eap_proxy.h" #include "eapol_supp_sm.h" @@ -95,7 +96,7 @@ struct eapol_sm { SUPP_BE_RECEIVE = 4, SUPP_BE_RESPONSE = 5, SUPP_BE_FAIL = 6, - SUPP_BE_TIMEOUT = 7, + SUPP_BE_TIMEOUT = 7, SUPP_BE_SUCCESS = 8 } SUPP_BE_state; /* dot1xSuppBackendPaeState */ /* Variables */ @@ -250,6 +251,8 @@ SM_STATE(SUPP_PAE, CONNECTING) if (sm->eapTriggerStart) send_start = 1; + if (sm->ctx->preauth) + send_start = 1; sm->eapTriggerStart = FALSE; if (send_start) { @@ -490,9 +493,24 @@ SM_STATE(SUPP_BE, SUCCESS) #ifdef CONFIG_EAP_PROXY if (sm->use_eap_proxy) { if (eap_proxy_key_available(sm->eap_proxy)) { + u8 *session_id, *emsk; + size_t session_id_len, emsk_len; + /* New key received - clear IEEE 802.1X EAPOL-Key replay * counter */ sm->replay_counter_valid = FALSE; + + session_id = eap_proxy_get_eap_session_id( + sm->eap_proxy, &session_id_len); + emsk = eap_proxy_get_emsk(sm->eap_proxy, &emsk_len); + if (sm->config->erp && session_id && emsk) { + eap_peer_erp_init(sm->eap, session_id, + session_id_len, emsk, + emsk_len); + } else { + os_free(session_id); + bin_clear_free(emsk, emsk_len); + } } return; } @@ -897,6 +915,9 @@ static void eapol_sm_abortSupp(struct eapol_sm *sm) wpabuf_free(sm->eapReqData); sm->eapReqData = NULL; eap_sm_abort(sm->eap); +#ifdef CONFIG_EAP_PROXY + eap_proxy_sm_abort(sm->eap_proxy); +#endif /* CONFIG_EAP_PROXY */ } @@ -1998,7 +2019,17 @@ static void eapol_sm_notify_status(void *ctx, const char *status, } +static void eapol_sm_notify_eap_error(void *ctx, int error_code) +{ + struct eapol_sm *sm = ctx; + + if (sm->ctx->eap_error_cb) + sm->ctx->eap_error_cb(sm->ctx->ctx, error_code); +} + + #ifdef CONFIG_EAP_PROXY + static void eapol_sm_eap_proxy_cb(void *ctx) { struct eapol_sm *sm = ctx; @@ -2006,6 +2037,18 @@ static void eapol_sm_eap_proxy_cb(void *ctx) if (sm->ctx->eap_proxy_cb) sm->ctx->eap_proxy_cb(sm->ctx->ctx); } + + +static void +eapol_sm_eap_proxy_notify_sim_status(void *ctx, + enum eap_proxy_sim_state sim_state) +{ + struct eapol_sm *sm = ctx; + + if (sm->ctx->eap_proxy_notify_sim_status) + sm->ctx->eap_proxy_notify_sim_status(sm->ctx->ctx, sim_state); +} + #endif /* CONFIG_EAP_PROXY */ @@ -2032,8 +2075,11 @@ static const struct eapol_callbacks eapol_cb = eapol_sm_eap_param_needed, eapol_sm_notify_cert, eapol_sm_notify_status, + eapol_sm_notify_eap_error, #ifdef CONFIG_EAP_PROXY eapol_sm_eap_proxy_cb, + eapol_sm_eap_proxy_notify_sim_status, + eapol_sm_get_eap_proxy_imsi, #endif /* CONFIG_EAP_PROXY */ eapol_sm_set_anon_id }; @@ -2141,16 +2187,16 @@ int eapol_sm_failed(struct eapol_sm *sm) } -int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len) -{ #ifdef CONFIG_EAP_PROXY +int eapol_sm_get_eap_proxy_imsi(void *ctx, int sim_num, char *imsi, size_t *len) +{ + struct eapol_sm *sm = ctx; + if (sm->eap_proxy == NULL) return -1; - return eap_proxy_get_imsi(sm->eap_proxy, imsi, len); -#else /* CONFIG_EAP_PROXY */ - return -1; -#endif /* CONFIG_EAP_PROXY */ + return eap_proxy_get_imsi(sm->eap_proxy, sim_num, imsi, len); } +#endif /* CONFIG_EAP_PROXY */ void eapol_sm_erp_flush(struct eapol_sm *sm) @@ -2158,3 +2204,56 @@ void eapol_sm_erp_flush(struct eapol_sm *sm) if (sm) eap_peer_erp_free_keys(sm->eap); } + + +struct wpabuf * eapol_sm_build_erp_reauth_start(struct eapol_sm *sm) +{ +#ifdef CONFIG_ERP + if (!sm) + return NULL; + return eap_peer_build_erp_reauth_start(sm->eap, 0); +#else /* CONFIG_ERP */ + return NULL; +#endif /* CONFIG_ERP */ +} + + +void eapol_sm_process_erp_finish(struct eapol_sm *sm, const u8 *buf, + size_t len) +{ +#ifdef CONFIG_ERP + if (!sm) + return; + eap_peer_finish(sm->eap, (const struct eap_hdr *) buf, len); +#endif /* CONFIG_ERP */ +} + + +int eapol_sm_update_erp_next_seq_num(struct eapol_sm *sm, u16 next_seq_num) +{ +#ifdef CONFIG_ERP + if (!sm) + return -1; + return eap_peer_update_erp_next_seq_num(sm->eap, next_seq_num); +#else /* CONFIG_ERP */ + return -1; +#endif /* CONFIG_ERP */ +} + + +int eapol_sm_get_erp_info(struct eapol_sm *sm, struct eap_peer_config *config, + const u8 **username, size_t *username_len, + const u8 **realm, size_t *realm_len, + u16 *erp_next_seq_num, const u8 **rrk, + size_t *rrk_len) +{ +#ifdef CONFIG_ERP + if (!sm) + return -1; + return eap_peer_get_erp_info(sm->eap, config, username, username_len, + realm, realm_len, erp_next_seq_num, rrk, + rrk_len); +#else /* CONFIG_ERP */ + return -1; +#endif /* CONFIG_ERP */ +} diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h index 1309ff7547e8..74f40bb1cd63 100644 --- a/src/eapol_supp/eapol_supp_sm.h +++ b/src/eapol_supp/eapol_supp_sm.h @@ -271,12 +271,27 @@ struct eapol_ctx { void (*status_cb)(void *ctx, const char *status, const char *parameter); + /** + * eap_error_cb - Notification of EAP method error + * @ctx: Callback context (ctx) + * @error_code: EAP method error code + */ + void (*eap_error_cb)(void *ctx, int error_code); + #ifdef CONFIG_EAP_PROXY /** * eap_proxy_cb - Callback signifying any updates from eap_proxy * @ctx: eapol_ctx from eap_peer_sm_init() call */ void (*eap_proxy_cb)(void *ctx); + + /** + * eap_proxy_notify_sim_status - Notification of SIM status change + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @status: One of enum value from sim_state + */ + void (*eap_proxy_notify_sim_status)(void *ctx, + enum eap_proxy_sim_state sim_state); #endif /* CONFIG_EAP_PROXY */ /** @@ -328,7 +343,18 @@ void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, struct ext_password_data *ext); int eapol_sm_failed(struct eapol_sm *sm); void eapol_sm_erp_flush(struct eapol_sm *sm); -int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len); +struct wpabuf * eapol_sm_build_erp_reauth_start(struct eapol_sm *sm); +void eapol_sm_process_erp_finish(struct eapol_sm *sm, const u8 *buf, + size_t len); +int eapol_sm_get_eap_proxy_imsi(void *ctx, int sim_num, char *imsi, + size_t *len); +int eapol_sm_update_erp_next_seq_num(struct eapol_sm *sm, u16 next_seq_num); +int eapol_sm_get_erp_info(struct eapol_sm *sm, struct eap_peer_config *config, + const u8 **username, size_t *username_len, + const u8 **realm, size_t *realm_len, + u16 *erp_next_seq_num, const u8 **rrk, + size_t *rrk_len); + #else /* IEEE8021X_EAPOL */ static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) { @@ -438,6 +464,28 @@ static inline int eapol_sm_failed(struct eapol_sm *sm) static inline void eapol_sm_erp_flush(struct eapol_sm *sm) { } +static inline struct wpabuf * +eapol_sm_build_erp_reauth_start(struct eapol_sm *sm) +{ + return NULL; +} +static inline void eapol_sm_process_erp_finish(struct eapol_sm *sm, + const u8 *buf, size_t len) +{ +} +static inline int eapol_sm_update_erp_next_seq_num(struct eapol_sm *sm, + u16 next_seq_num) +{ + return -1; +} +static inline int +eapol_sm_get_erp_info(struct eapol_sm *sm, struct eap_peer_config *config, + const u8 **username, size_t *username_len, + const u8 **realm, size_t *realm_len, + u16 *erp_next_seq_num, const u8 **rrk, size_t *rrk_len) +{ + return -1; +} #endif /* IEEE8021X_EAPOL */ #endif /* EAPOL_SUPP_SM_H */ diff --git a/src/fst/fst_ctrl_aux.h b/src/fst/fst_ctrl_aux.h index e2133f5062bd..0aff5d061eae 100644 --- a/src/fst/fst_ctrl_aux.h +++ b/src/fst/fst_ctrl_aux.h @@ -35,6 +35,17 @@ enum fst_event_type { * more info */ }; +enum fst_reason { + REASON_TEARDOWN, + REASON_SETUP, + REASON_SWITCH, + REASON_STT, + REASON_REJECT, + REASON_ERROR_PARAMS, + REASON_RESET, + REASON_DETACH_IFACE, +}; + enum fst_initiator { FST_INITIATOR_UNDEFINED, FST_INITIATOR_LOCAL, @@ -57,16 +68,7 @@ union fst_event_extra { enum fst_session_state new_state; union fst_session_state_switch_extra { struct { - enum fst_reason { - REASON_TEARDOWN, - REASON_SETUP, - REASON_SWITCH, - REASON_STT, - REASON_REJECT, - REASON_ERROR_PARAMS, - REASON_RESET, - REASON_DETACH_IFACE, - } reason; + enum fst_reason reason; u8 reject_code; /* REASON_REJECT */ /* REASON_SWITCH, * REASON_TEARDOWN, diff --git a/src/fst/fst_ctrl_iface.c b/src/fst/fst_ctrl_iface.c index 7820e586629f..7df3362b6e93 100644 --- a/src/fst/fst_ctrl_iface.c +++ b/src/fst/fst_ctrl_iface.c @@ -49,7 +49,7 @@ static Boolean format_session_state_extra(const union fst_event_extra *extra, if (ss->extra.to_initial.reject_code != WLAN_STATUS_SUCCESS) os_snprintf(reject_str, sizeof(reject_str), "%u", ss->extra.to_initial.reject_code); - /* no break */ + /* fall through */ case REASON_TEARDOWN: case REASON_SWITCH: switch (ss->extra.to_initial.initiator) { diff --git a/src/fst/fst_group.c b/src/fst/fst_group.c index 321d40d50cd2..a4ae016d9163 100644 --- a/src/fst/fst_group.c +++ b/src/fst/fst_group.c @@ -29,7 +29,7 @@ static void fst_dump_mb_ies(const char *group_id, const char *ifname, const struct multi_band_ie *mbie = (const struct multi_band_ie *) p; WPA_ASSERT(mbie->eid == WLAN_EID_MULTI_BAND); - WPA_ASSERT(2 + mbie->len >= sizeof(*mbie)); + WPA_ASSERT(2U + mbie->len >= sizeof(*mbie)); fst_printf(MSG_WARNING, "%s: %s: mb_ctrl=%u band_id=%u op_class=%u chan=%u bssid=" diff --git a/src/fst/fst_iface.h b/src/fst/fst_iface.h index 0eb27325a2b8..cbaa7d81788d 100644 --- a/src/fst/fst_iface.h +++ b/src/fst/fst_iface.h @@ -106,7 +106,7 @@ static inline void fst_iface_update_mb_ie(struct fst_iface *i, const u8 *addr, const u8 *buf, size_t size) { - return i->iface_obj.update_mb_ie(i->iface_obj.ctx, addr, buf, size); + i->iface_obj.update_mb_ie(i->iface_obj.ctx, addr, buf, size); } static inline const u8 * fst_iface_get_peer_first(struct fst_iface *i, diff --git a/src/fst/fst_session.c b/src/fst/fst_session.c index 76e2c78f4ff6..a02a93e76e43 100644 --- a/src/fst/fst_session.c +++ b/src/fst/fst_session.c @@ -756,8 +756,6 @@ struct fst_session * fst_session_create(struct fst_group *g) struct fst_session *s; u32 id; - WPA_ASSERT(!is_zero_ether_addr(own_addr)); - id = fst_find_free_session_id(); if (id == FST_INVALID_SESSION_ID) { fst_printf(MSG_ERROR, "Cannot assign new session ID"); diff --git a/src/l2_packet/l2_packet.h b/src/l2_packet/l2_packet.h index 2a452458214b..53871774b4e7 100644 --- a/src/l2_packet/l2_packet.h +++ b/src/l2_packet/l2_packet.h @@ -42,6 +42,7 @@ struct l2_ethhdr { enum l2_packet_filter_type { L2_PACKET_FILTER_DHCP, L2_PACKET_FILTER_NDISC, + L2_PACKET_FILTER_PKTTYPE, }; /** diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c index a7a300e568e6..291c9dd263a6 100644 --- a/src/l2_packet/l2_packet_linux.c +++ b/src/l2_packet/l2_packet_linux.c @@ -84,6 +84,26 @@ static const struct sock_fprog ndisc_sock_filter = { .filter = ndisc_sock_filter_insns, }; +/* drop packet if skb->pkt_type is PACKET_OTHERHOST (0x03). Generated by: + * $ bpfc - <<EOF + * > ldb #type + * > jeq #0x03, drop + * > pass: ret #-1 + * > drop: ret #0 + * > EOF + */ +static struct sock_filter pkt_type_filter_insns[] = { + { 0x30, 0, 0, 0xfffff004 }, + { 0x15, 1, 0, 0x00000003 }, + { 0x6, 0, 0, 0xffffffff }, + { 0x6, 0, 0, 0x00000000 }, +}; + +static const struct sock_fprog pkt_type_sock_filter = { + .len = ARRAY_SIZE(pkt_type_filter_insns), + .filter = pkt_type_filter_insns, +}; + int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) { @@ -96,6 +116,9 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, const u8 *buf, size_t len) { int ret; + + if (TEST_FAIL()) + return -1; if (l2 == NULL) return -1; if (l2->l2_hdr) { @@ -458,6 +481,9 @@ int l2_packet_set_packet_filter(struct l2_packet_data *l2, { const struct sock_fprog *sock_filter; + if (TEST_FAIL()) + return -1; + switch (type) { case L2_PACKET_FILTER_DHCP: sock_filter = &dhcp_sock_filter; @@ -465,6 +491,9 @@ int l2_packet_set_packet_filter(struct l2_packet_data *l2, case L2_PACKET_FILTER_NDISC: sock_filter = &ndisc_sock_filter; break; + case L2_PACKET_FILTER_PKTTYPE: + sock_filter = &pkt_type_sock_filter; + break; default: return -1; } diff --git a/src/l2_packet/l2_packet_privsep.c b/src/l2_packet/l2_packet_privsep.c index e26ca20a8625..ce86802c2353 100644 --- a/src/l2_packet/l2_packet_privsep.c +++ b/src/l2_packet/l2_packet_privsep.c @@ -51,7 +51,7 @@ static int wpa_priv_cmd(struct l2_packet_data *l2, int cmd, return 0; } - + int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) { os_memcpy(addr, l2->own_addr, ETH_ALEN); @@ -258,7 +258,7 @@ void l2_packet_deinit(struct l2_packet_data *l2) unlink(l2->own_socket_path); os_free(l2->own_socket_path); } - + os_free(l2); } diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 996b4e824986..b4660c4f9616 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -711,6 +711,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, struct p2p_message msg; const u8 *p2p_dev_addr; int wfd_changed; + int dev_name_changed; int i; struct os_reltime time_now; @@ -821,6 +822,9 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, } dev->info.level = level; + dev_name_changed = os_strncmp(dev->info.device_name, msg.device_name, + WPS_DEV_NAME_MAX_LEN) != 0; + p2p_copy_wps_info(p2p, dev, 0, &msg); for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { @@ -839,9 +843,12 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, wfd_changed = p2p_compare_wfd_info(dev, &msg); - if (msg.wfd_subelems) { + if (wfd_changed) { wpabuf_free(dev->info.wfd_subelems); - dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + if (msg.wfd_subelems) + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + else + dev->info.wfd_subelems = NULL; } if (scan_res) { @@ -855,6 +862,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, p2p_update_peer_vendor_elems(dev, ies, ies_len); if (dev->flags & P2P_DEV_REPORTED && !wfd_changed && + !dev_name_changed && (!msg.adv_service_instance || (dev->flags & P2P_DEV_P2PS_REPORTED))) return 0; @@ -1002,8 +1010,16 @@ static void p2p_search(struct p2p_data *p2p) } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); - if (p2p->find_type == P2P_FIND_PROGRESSIVE && - (freq = p2p_get_next_prog_freq(p2p)) > 0) { + if (p2p->find_pending_full && + (p2p->find_type == P2P_FIND_PROGRESSIVE || + p2p->find_type == P2P_FIND_START_WITH_FULL)) { + type = P2P_SCAN_FULL; + p2p_dbg(p2p, "Starting search (pending full scan)"); + p2p->find_pending_full = 0; + } else if ((p2p->find_type == P2P_FIND_PROGRESSIVE && + (freq = p2p_get_next_prog_freq(p2p)) > 0) || + (p2p->find_type == P2P_FIND_START_WITH_FULL && + (freq = p2p->find_specified_freq) > 0)) { type = P2P_SCAN_SOCIAL_PLUS_ONE; p2p_dbg(p2p, "Starting search (+ freq %u)", freq); } else { @@ -1165,12 +1181,11 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, p2p_free_req_dev_types(p2p); if (req_dev_types && num_req_dev_types) { - p2p->req_dev_types = os_malloc(num_req_dev_types * + p2p->req_dev_types = os_memdup(req_dev_types, + num_req_dev_types * WPS_DEV_TYPE_LEN); if (p2p->req_dev_types == NULL) return -1; - os_memcpy(p2p->req_dev_types, req_dev_types, - num_req_dev_types * WPS_DEV_TYPE_LEN); p2p->num_req_dev_types = num_req_dev_types; } @@ -1227,7 +1242,12 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, p2p->pending_listen_freq = 0; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p->find_pending_full = 0; p2p->find_type = type; + if (freq != 2412 && freq != 2437 && freq != 2462 && freq != 60480) + p2p->find_specified_freq = freq; + else + p2p->find_specified_freq = 0; p2p_device_clear_reported(p2p); os_memset(p2p->sd_query_no_ack, 0, ETH_ALEN); p2p_set_state(p2p, P2P_SEARCH); @@ -1243,7 +1263,7 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, if (freq > 0) { /* * Start with the specified channel and then move to - * social channels only scans. + * scans for social channels and this specific channel. */ res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SPECIFIC, freq, @@ -1272,6 +1292,9 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, if (res != 0 && p2p->p2p_scan_running) { p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running"); /* wait for the previous p2p_scan to complete */ + if (type == P2P_FIND_PROGRESSIVE || + (type == P2P_FIND_START_WITH_FULL && freq == 0)) + p2p->find_pending_full = 1; res = 0; /* do not report failure */ } else if (res != 0) { p2p_dbg(p2p, "Failed to start p2p_scan"); @@ -1833,6 +1856,7 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) p2p_clear_timeout(p2p); p2p->ssid_set = 0; peer->go_neg_req_sent = 0; + peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE; peer->wps_method = WPS_NOT_READY; peer->oob_pw_id = 0; wpabuf_free(peer->go_neg_conf); @@ -2822,6 +2846,7 @@ void p2p_service_flush_asp(struct p2p_data *p2p) } p2p->p2ps_adv_list = NULL; + p2ps_prov_free(p2p); p2p_dbg(p2p, "All ASP advertisements flushed"); } @@ -2983,6 +3008,7 @@ void p2p_deinit(struct p2p_data *p2p) wpabuf_free(p2p->wfd_dev_info); wpabuf_free(p2p->wfd_assoc_bssid); wpabuf_free(p2p->wfd_coupled_sink_info); + wpabuf_free(p2p->wfd_r2_dev_info); #endif /* CONFIG_WIFI_DISPLAY */ eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); @@ -3022,6 +3048,10 @@ void p2p_flush(struct p2p_data *p2p) os_free(p2p->after_scan_tx); p2p->after_scan_tx = NULL; p2p->ssid_set = 0; + p2ps_prov_free(p2p); + p2p_reset_pending_pd(p2p); + p2p->override_pref_op_class = 0; + p2p->override_pref_channel = 0; } @@ -3859,6 +3889,19 @@ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) if (p2p->in_listen) return 0; /* Internal timeout will trigger the next step */ + if (p2p->state == P2P_WAIT_PEER_CONNECT && p2p->go_neg_peer && + p2p->pending_listen_freq) { + /* + * Better wait a bit if the driver is unable to start + * offchannel operation for some reason to continue with + * P2P_WAIT_PEER_(IDLE/CONNECT) state transitions. + */ + p2p_dbg(p2p, + "Listen operation did not seem to start - delay idle phase to avoid busy loop"); + p2p_set_timeout(p2p, 0, 100000); + return 1; + } + if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) { if (p2p->go_neg_peer->connect_reqs >= 120) { p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response"); @@ -4803,11 +4846,10 @@ int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, struct p2p_channel *n; if (pref_chan) { - n = os_malloc(num_pref_chan * sizeof(struct p2p_channel)); + n = os_memdup(pref_chan, + num_pref_chan * sizeof(struct p2p_channel)); if (n == NULL) return -1; - os_memcpy(n, pref_chan, - num_pref_chan * sizeof(struct p2p_channel)); } else n = NULL; @@ -5129,6 +5171,20 @@ int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem) } +int p2p_set_wfd_r2_dev_info(struct p2p_data *p2p, const struct wpabuf *elem) +{ + wpabuf_free(p2p->wfd_r2_dev_info); + if (elem) { + p2p->wfd_r2_dev_info = wpabuf_dup(elem); + if (p2p->wfd_r2_dev_info == NULL) + return -1; + } else + p2p->wfd_r2_dev_info = NULL; + + return 0; +} + + int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem) { wpabuf_free(p2p->wfd_assoc_bssid); @@ -5510,6 +5566,14 @@ void p2p_set_own_pref_freq_list(struct p2p_data *p2p, } +void p2p_set_override_pref_op_chan(struct p2p_data *p2p, u8 op_class, + u8 chan) +{ + p2p->override_pref_op_class = op_class; + p2p->override_pref_channel = chan; +} + + struct wpabuf * p2p_build_probe_resp_template(struct p2p_data *p2p, unsigned int freq) { diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index 7b18dcfc3ff3..fac5ce05ae6e 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -2266,6 +2266,7 @@ int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem); +int p2p_set_wfd_r2_dev_info(struct p2p_data *p2p, const struct wpabuf *elem); int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem); int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p, const struct wpabuf *elem); @@ -2373,6 +2374,8 @@ void p2p_expire_peers(struct p2p_data *p2p); void p2p_set_own_pref_freq_list(struct p2p_data *p2p, const unsigned int *pref_freq_list, unsigned int size); +void p2p_set_override_pref_op_chan(struct p2p_data *p2p, u8 op_class, + u8 chan); /** * p2p_group_get_common_freqs - Get the group common frequencies diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index 9f0b3f3d37a4..65ab4b8d3fd6 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -315,7 +315,12 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, group_capab); p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker); p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); - if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) { + if (p2p->override_pref_op_class) { + p2p_dbg(p2p, "Override operating channel preference"); + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->override_pref_op_class, + p2p->override_pref_channel); + } else if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) { p2p_dbg(p2p, "Omit Operating Channel attribute"); } else { p2p_buf_add_operating_channel(buf, p2p->cfg->country, @@ -562,26 +567,11 @@ static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go, * also supported by the peer device. */ for (i = 0; i < size && !found; i++) { - /* - * Make sure that the common frequency is: - * 1. Supported by peer - * 2. Allowed for P2P use. - */ + /* Make sure that the common frequency is supported by peer. */ oper_freq = freq_list[i]; if (p2p_freq_to_channel(oper_freq, &op_class, - &op_channel) < 0) { - p2p_dbg(p2p, "Unsupported frequency %u MHz", oper_freq); - continue; - } - if (!p2p_channels_includes(&p2p->cfg->channels, - op_class, op_channel) && - (go || !p2p_channels_includes(&p2p->cfg->cli_channels, - op_class, op_channel))) { - p2p_dbg(p2p, - "Freq %u MHz (oper_class %u channel %u) not allowed for P2P", - oper_freq, op_class, op_channel); - break; - } + &op_channel) < 0) + continue; /* cannot happen due to earlier check */ for (j = 0; j < msg->channel_list_len; j++) { if (op_channel != msg->channel_list[j]) @@ -602,8 +592,7 @@ static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go, oper_freq); } else { p2p_dbg(p2p, - "None of our preferred channels are supported by peer!. Use: %d MHz for oper_channel", - dev->oper_freq); + "None of our preferred channels are supported by peer!"); } } @@ -629,29 +618,9 @@ static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go, msg->pref_freq_list[2 * j + 1]); if (freq_list[i] != oper_freq) continue; - - /* - * Make sure that the found frequency is: - * 1. Supported - * 2. Allowed for P2P use. - */ if (p2p_freq_to_channel(oper_freq, &op_class, - &op_channel) < 0) { - p2p_dbg(p2p, "Unsupported frequency %u MHz", - oper_freq); - continue; - } - - if (!p2p_channels_includes(&p2p->cfg->channels, - op_class, op_channel) && - (go || - !p2p_channels_includes(&p2p->cfg->cli_channels, - op_class, op_channel))) { - p2p_dbg(p2p, - "Freq %u MHz (oper_class %u channel %u) not allowed for P2P", - oper_freq, op_class, op_channel); - break; - } + &op_channel) < 0) + continue; /* cannot happen */ p2p->op_reg_class = op_class; p2p->op_channel = op_channel; os_memcpy(&p2p->channels, &p2p->cfg->channels, @@ -666,9 +635,7 @@ static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go, "Freq %d MHz is a common preferred channel for both peer and local, use it as operating channel", oper_freq); } else { - p2p_dbg(p2p, - "No common preferred channels found! Use: %d MHz for oper_channel", - dev->oper_freq); + p2p_dbg(p2p, "No common preferred channels found!"); } } @@ -679,6 +646,8 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, unsigned int freq_list[P2P_MAX_PREF_CHANNELS], size; unsigned int i; u8 op_class, op_channel; + char txt[100], *pos, *end; + int res; /* * Use the preferred channel list from the driver only if there is no @@ -694,6 +663,39 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, &size, freq_list)) return; + /* Filter out frequencies that are not acceptable for P2P use */ + i = 0; + while (i < size) { + if (p2p_freq_to_channel(freq_list[i], &op_class, + &op_channel) < 0 || + (!p2p_channels_includes(&p2p->cfg->channels, + op_class, op_channel) && + (go || !p2p_channels_includes(&p2p->cfg->cli_channels, + op_class, op_channel)))) { + p2p_dbg(p2p, + "Ignore local driver frequency preference %u MHz since it is not acceptable for P2P use (go=%d)", + freq_list[i], go); + if (size - i - 1 > 0) + os_memmove(&freq_list[i], &freq_list[i + 1], size - i - 1); + size--; + continue; + } + + /* Preferred frequency is acceptable for P2P use */ + i++; + } + + pos = txt; + end = pos + sizeof(txt); + for (i = 0; i < size; i++) { + res = os_snprintf(pos, end - pos, " %u", freq_list[i]); + if (os_snprintf_error(end - pos, res)) + break; + pos += res; + } + *pos = '\0'; + p2p_dbg(p2p, "Local driver frequency preference (size=%u):%s", + size, txt); /* * Check if peer's preference of operating channel is in @@ -703,20 +705,14 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, if (freq_list[i] == (unsigned int) dev->oper_freq) break; } - if (i != size) { + if (i != size && + p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) == 0) { /* Peer operating channel preference matches our preference */ - if (p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) < - 0) { - p2p_dbg(p2p, - "Peer operating channel preference is unsupported frequency %u MHz", - freq_list[i]); - } else { - p2p->op_reg_class = op_class; - p2p->op_channel = op_channel; - os_memcpy(&p2p->channels, &p2p->cfg->channels, - sizeof(struct p2p_channels)); - return; - } + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + return; } p2p_dbg(p2p, diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c index 051b4e391505..16c28a0d95ee 100644 --- a/src/p2p/p2p_group.c +++ b/src/p2p/p2p_group.c @@ -367,6 +367,8 @@ wifi_display_build_go_ie(struct p2p_group *group) return NULL; if (group->p2p->wfd_dev_info) wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info); + if (group->p2p->wfd_r2_dev_info) + wpabuf_put_buf(wfd_subelems, group->p2p->wfd_r2_dev_info); if (group->p2p->wfd_assoc_bssid) wpabuf_put_buf(wfd_subelems, group->p2p->wfd_assoc_bssid); diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 47524d4991a5..6a4d751c090a 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -437,9 +437,11 @@ struct p2p_data { int inv_persistent; enum p2p_discovery_type find_type; + int find_specified_freq; unsigned int last_p2p_find_timeout; u8 last_prog_scan_class; u8 last_prog_scan_chan; + unsigned int find_pending_full:1; int p2p_scan_running; enum p2p_after_scan { P2P_AFTER_SCAN_NOTHING, @@ -545,6 +547,7 @@ struct p2p_data { struct wpabuf *wfd_dev_info; struct wpabuf *wfd_assoc_bssid; struct wpabuf *wfd_coupled_sink_info; + struct wpabuf *wfd_r2_dev_info; #endif /* CONFIG_WIFI_DISPLAY */ u16 authorized_oob_dev_pw_id; @@ -553,6 +556,10 @@ struct p2p_data { unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS]; unsigned int num_pref_freq; + + /* Override option for preferred operating channel in GO Negotiation */ + u8 override_pref_op_class; + u8 override_pref_channel; }; /** diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c index 93a0535f873a..3994ec03f86b 100644 --- a/src/p2p/p2p_pd.c +++ b/src/p2p/p2p_pd.c @@ -1163,6 +1163,9 @@ out: msg.group_id, msg.group_id_len); } + if (reject != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) + p2ps_prov_free(p2p); + if (reject == P2P_SC_SUCCESS) { switch (config_methods) { case WPS_CONFIG_DISPLAY: @@ -1581,7 +1584,7 @@ out: report_config_methods); if (p2p->state == P2P_PD_DURING_FIND) { - p2p_clear_timeout(p2p); + p2p_stop_listen_for_freq(p2p, 0); p2p_continue_find(p2p); } } diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c index a8bc5ba7f344..b9e753f20516 100644 --- a/src/p2p/p2p_sd.c +++ b/src/p2p/p2p_sd.c @@ -426,6 +426,7 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, { struct wpabuf *resp; size_t max_len; + unsigned int wait_time = 200; /* * In the 60 GHz, we have a smaller maximum frame length for management @@ -460,6 +461,7 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, 1, p2p->srv_update_indic, NULL); } else { p2p_dbg(p2p, "SD response fits in initial response"); + wait_time = 0; /* no more SD frames in the sequence */ resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS, 0, p2p->srv_update_indic, resp_tlvs); @@ -470,7 +472,7 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 200) < 0) + wpabuf_head(resp), wpabuf_len(resp), wait_time) < 0) p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(resp); @@ -623,6 +625,7 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, u8 dialog_token; size_t frag_len, max_len; int more = 0; + unsigned int wait_time = 200; wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len); if (len < 1) @@ -675,12 +678,13 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "All fragments of SD response sent"); wpabuf_free(p2p->sd_resp); p2p->sd_resp = NULL; + wait_time = 0; /* no more SD frames in the sequence */ } p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 200) < 0) + wpabuf_head(resp), wpabuf_len(resp), wait_time) < 0) p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(resp); diff --git a/src/pae/ieee802_1x_cp.c b/src/pae/ieee802_1x_cp.c index e294e6466285..360fcd3f5fcd 100644 --- a/src/pae/ieee802_1x_cp.c +++ b/src/pae/ieee802_1x_cp.c @@ -159,6 +159,7 @@ SM_STATE(CP, ALLOWED) secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled); secy_cp_control_protect_frames(sm->kay, sm->protect_frames); + secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt); secy_cp_control_validate_frames(sm->kay, sm->validate_frames); secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window); } @@ -177,6 +178,7 @@ SM_STATE(CP, AUTHENTICATED) secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled); secy_cp_control_protect_frames(sm->kay, sm->protect_frames); + secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt); secy_cp_control_validate_frames(sm->kay, sm->validate_frames); secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window); } @@ -203,6 +205,7 @@ SM_STATE(CP, SECURED) secy_cp_control_confidentiality_offset(sm->kay, sm->confidentiality_offset); secy_cp_control_protect_frames(sm->kay, sm->protect_frames); + secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt); secy_cp_control_validate_frames(sm->kay, sm->validate_frames); secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window); } @@ -466,6 +469,7 @@ struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay) wpa_printf(MSG_DEBUG, "CP: state machine created"); secy_cp_control_protect_frames(sm->kay, sm->protect_frames); + secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt); secy_cp_control_validate_frames(sm->kay, sm->validate_frames); secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window); secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled); diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c index a8e7efc9b3bd..cda23fcab41a 100644 --- a/src/pae/ieee802_1x_kay.c +++ b/src/pae/ieee802_1x_kay.c @@ -45,6 +45,14 @@ static struct macsec_ciphersuite cipher_suite_tbl[] = { .sak_len = DEFAULT_SA_KEY_LEN, .index = 0, }, + /* GCM-AES-256 */ + { + .id = CS_ID_GCM_AES_256, + .name = CS_NAME_GCM_AES_256, + .capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50, + .sak_len = 32, + .index = 1 /* index */ + }, }; #define CS_TABLE_SIZE (ARRAY_SIZE(cipher_suite_tbl)) #define DEFAULT_CS_INDEX 0 @@ -245,13 +253,15 @@ ieee802_1x_mka_dump_sak_use_body(struct ieee802_1x_mka_sak_use_body *body) * ieee802_1x_kay_get_participant - */ static struct ieee802_1x_mka_participant * -ieee802_1x_kay_get_participant(struct ieee802_1x_kay *kay, const u8 *ckn) +ieee802_1x_kay_get_participant(struct ieee802_1x_kay *kay, const u8 *ckn, + size_t len) { struct ieee802_1x_mka_participant *participant; dl_list_for_each(participant, &kay->participant_list, struct ieee802_1x_mka_participant, list) { - if (os_memcmp(participant->ckn.name, ckn, + if (participant->ckn.len == len && + os_memcmp(participant->ckn.name, ckn, participant->ckn.len) == 0) return participant; } @@ -379,6 +389,17 @@ ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant, } +u64 mka_sci_u64(struct ieee802_1x_mka_sci *sci) +{ + struct ieee802_1x_mka_sci tmp; + + os_memcpy(tmp.addr, sci->addr, ETH_ALEN); + tmp.port = sci->port; + + return *((u64 *) &tmp); +} + + static Boolean sci_equal(const struct ieee802_1x_mka_sci *a, const struct ieee802_1x_mka_sci *b) { @@ -411,6 +432,8 @@ ieee802_1x_kay_get_peer_sci(struct ieee802_1x_mka_participant *participant, } +static void ieee802_1x_kay_use_data_key(struct data_key *pkey); + /** * ieee802_1x_kay_init_receive_sa - */ @@ -429,6 +452,7 @@ ieee802_1x_kay_init_receive_sa(struct receive_sc *psc, u8 an, u32 lowest_pn, return NULL; } + ieee802_1x_kay_use_data_key(key); psa->pkey = key; psa->lowest_pn = lowest_pn; psa->next_pn = lowest_pn; @@ -440,18 +464,21 @@ ieee802_1x_kay_init_receive_sa(struct receive_sc *psc, u8 an, u32 lowest_pn, dl_list_add(&psc->sa_list, &psa->list); wpa_printf(MSG_DEBUG, - "KaY: Create receive SA(AN: %hhu lowest_pn: %u of SC(channel: %d)", - an, lowest_pn, psc->channel); + "KaY: Create receive SA(AN: %hhu lowest_pn: %u of SC", + an, lowest_pn); return psa; } +static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey); + /** * ieee802_1x_kay_deinit_receive_sa - */ static void ieee802_1x_kay_deinit_receive_sa(struct receive_sa *psa) { + ieee802_1x_kay_deinit_data_key(psa->pkey); psa->pkey = NULL; wpa_printf(MSG_DEBUG, "KaY: Delete receive SA(an: %hhu) of SC", @@ -465,8 +492,7 @@ static void ieee802_1x_kay_deinit_receive_sa(struct receive_sa *psa) * ieee802_1x_kay_init_receive_sc - */ static struct receive_sc * -ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci, - int channel) +ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci) { struct receive_sc *psc; @@ -480,19 +506,27 @@ ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci, } os_memcpy(&psc->sci, psci, sizeof(psc->sci)); - psc->channel = channel; os_get_time(&psc->created_time); psc->receiving = FALSE; dl_list_init(&psc->sa_list); - wpa_printf(MSG_DEBUG, "KaY: Create receive SC(channel: %d)", channel); + wpa_printf(MSG_DEBUG, "KaY: Create receive SC"); wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)psci, sizeof(*psci)); return psc; } +static void ieee802_1x_delete_receive_sa(struct ieee802_1x_kay *kay, + struct receive_sa *sa) +{ + secy_disable_receive_sa(kay, sa); + secy_delete_receive_sa(kay, sa); + ieee802_1x_kay_deinit_receive_sa(sa); +} + + /** * ieee802_1x_kay_deinit_receive_sc - **/ @@ -502,14 +536,13 @@ ieee802_1x_kay_deinit_receive_sc( { struct receive_sa *psa, *pre_sa; - wpa_printf(MSG_DEBUG, "KaY: Delete receive SC(channel: %d)", - psc->channel); + wpa_printf(MSG_DEBUG, "KaY: Delete receive SC"); dl_list_for_each_safe(psa, pre_sa, &psc->sa_list, struct receive_sa, - list) { - secy_disable_receive_sa(participant->kay, psa); - ieee802_1x_kay_deinit_receive_sa(psa); - } + list) + ieee802_1x_delete_receive_sa(participant->kay, psa); + dl_list_del(&psc->list); + secy_delete_receive_sc(participant->kay, psc); os_free(psc); } @@ -552,7 +585,6 @@ ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant, { struct ieee802_1x_kay_peer *peer; struct receive_sc *rxsc; - u32 sc_ch = 0; peer = ieee802_1x_kay_create_peer(mi, mn); if (!peer) @@ -561,9 +593,7 @@ ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant, os_memcpy(&peer->sci, &participant->current_peer_sci, sizeof(peer->sci)); - secy_get_available_receive_sc(participant->kay, &sc_ch); - - rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci, sc_ch); + rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci); if (!rxsc) { os_free(peer); return NULL; @@ -611,12 +641,12 @@ ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant, { struct ieee802_1x_kay_peer *peer; struct receive_sc *rxsc; - u32 sc_ch = 0; peer = ieee802_1x_kay_get_potential_peer(participant, mi); + if (!peer) + return NULL; - rxsc = ieee802_1x_kay_init_receive_sc(&participant->current_peer_sci, - sc_ch); + rxsc = ieee802_1x_kay_init_receive_sc(&participant->current_peer_sci); if (!rxsc) return NULL; @@ -631,8 +661,6 @@ ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant, dl_list_del(&peer->list); dl_list_add_tail(&participant->live_peers, &peer->list); - secy_get_available_receive_sc(participant->kay, &sc_ch); - dl_list_add(&participant->rxsc_list, &rxsc->list); secy_create_receive_sc(participant->kay, rxsc); @@ -730,6 +758,8 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, struct ieee802_1x_mka_participant *participant; const struct ieee802_1x_mka_basic_body *body; struct ieee802_1x_kay_peer *peer; + size_t ckn_len; + size_t body_len; body = (const struct ieee802_1x_mka_basic_body *) mka_msg; @@ -743,7 +773,15 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, return NULL; } - participant = ieee802_1x_kay_get_participant(kay, body->ckn); + body_len = get_mka_param_body_len(body); + if (body_len < sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN) { + wpa_printf(MSG_DEBUG, "KaY: Too small body length %zu", + body_len); + return NULL; + } + ckn_len = body_len - + (sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN); + participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len); if (!participant) { wpa_printf(MSG_DEBUG, "Peer is not included in my CA"); return NULL; @@ -946,21 +984,19 @@ ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant, body_len = get_mka_param_body_len(hdr); body_type = get_mka_param_body_type(hdr); - if (body_type != MKA_LIVE_PEER_LIST && - body_type != MKA_POTENTIAL_PEER_LIST) - continue; - - ieee802_1x_mka_dump_peer_body( - (struct ieee802_1x_mka_peer_body *)pos); - - if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) { + if (left_len < (MKA_HDR_LEN + MKA_ALIGN_LENGTH(body_len) + DEFAULT_ICV_LEN)) { wpa_printf(MSG_ERROR, "KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV", left_len, MKA_HDR_LEN, - body_len, DEFAULT_ICV_LEN); - continue; + MKA_ALIGN_LENGTH(body_len), + DEFAULT_ICV_LEN); + return FALSE; } + if (body_type != MKA_LIVE_PEER_LIST && + body_type != MKA_POTENTIAL_PEER_LIST) + continue; + if ((body_len % 16) != 0) { wpa_printf(MSG_ERROR, "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets", @@ -968,6 +1004,9 @@ ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant, continue; } + ieee802_1x_mka_dump_peer_body( + (struct ieee802_1x_mka_peer_body *)pos); + for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) { const struct ieee802_1x_mka_peer_id *peer_mi; @@ -1312,8 +1351,8 @@ ieee802_1x_mka_decode_sak_use_body( } } - /* check old key is valid */ - if (body->otx || body->orx) { + /* check old key is valid (but only if we remember our old key) */ + if (participant->oki.kn != 0 && (body->otx || body->orx)) { if (os_memcmp(participant->oki.mi, body->osrv_mi, sizeof(participant->oki.mi)) != 0 || be_to_host32(body->okn) != participant->oki.kn || @@ -1544,7 +1583,7 @@ ieee802_1x_mka_decode_dist_sak_body( ieee802_1x_cp_connect_authenticated(kay->cp); ieee802_1x_cp_sm_step(kay->cp); wpa_printf(MSG_WARNING, "KaY:The Key server advise no MACsec"); - participant->to_use_sak = TRUE; + participant->to_use_sak = FALSE; return 0; } @@ -1595,7 +1634,8 @@ ieee802_1x_mka_decode_dist_sak_body( os_free(unwrap_sak); return -1; } - wpa_hexdump(MSG_DEBUG, "\tAES Key Unwrap of SAK:", unwrap_sak, sak_len); + wpa_hexdump_key(MSG_DEBUG, "\tAES Key Unwrap of SAK:", + unwrap_sak, sak_len); sa_key = os_zalloc(sizeof(*sa_key)); if (!sa_key) { @@ -1614,6 +1654,7 @@ ieee802_1x_mka_decode_dist_sak_body( sa_key->an = body->dan; ieee802_1x_kay_init_data_key(sa_key); + ieee802_1x_kay_use_data_key(sa_key); dl_list_add(&participant->sak_list, &sa_key->list); ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id); @@ -1625,6 +1666,7 @@ ieee802_1x_mka_decode_dist_sak_body( ieee802_1x_cp_signal_newsak(kay->cp); ieee802_1x_cp_sm_step(kay->cp); + kay->rcvd_keys++; participant->to_use_sak = TRUE; return 0; @@ -1875,7 +1917,17 @@ static struct mka_param_body_handler mka_body_handler[] = { /** - * ieee802_1x_kay_deinit_data_key - + * ieee802_1x_kay_use_data_key - Take reference on a key + */ +static void ieee802_1x_kay_use_data_key(struct data_key *pkey) +{ + pkey->user++; +} + + +/** + * ieee802_1x_kay_deinit_data_key - Release reference on a key and + * free if there are no remaining users */ static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey) { @@ -1886,7 +1938,6 @@ static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey) if (pkey->user > 1) return; - dl_list_del(&pkey->list); os_free(pkey->key); os_free(pkey); } @@ -1975,7 +2026,7 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) wpa_printf(MSG_ERROR, "KaY: SAK Length not support"); goto fail; } - wpa_hexdump(MSG_DEBUG, "KaY: generated new SAK", key, key_len); + wpa_hexdump_key(MSG_DEBUG, "KaY: generated new SAK", key, key_len); os_free(context); context = NULL; @@ -1996,7 +2047,9 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) participant->new_key = sa_key; + ieee802_1x_kay_use_data_key(sa_key); dl_list_add(&participant->sak_list, &sa_key->list); + ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id); ieee802_1x_cp_sm_step(kay->cp); ieee802_1x_cp_set_offset(kay->cp, kay->macsec_confidentiality); @@ -2049,6 +2102,7 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) struct ieee802_1x_kay_peer *key_server = NULL; struct ieee802_1x_kay *kay = participant->kay; Boolean i_is_key_server; + int priority_comparison; if (participant->is_obliged_key_server) { participant->new_sak = TRUE; @@ -2079,8 +2133,14 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) tmp.key_server_priority = kay->actor_priority; os_memcpy(&tmp.sci, &kay->actor_sci, sizeof(tmp.sci)); - if (compare_priorities(&tmp, key_server) < 0) + priority_comparison = compare_priorities(&tmp, key_server); + if (priority_comparison < 0) { i_is_key_server = TRUE; + } else if (priority_comparison == 0) { + wpa_printf(MSG_WARNING, + "KaY: Cannot elect key server between me and peer, duplicate MAC detected"); + key_server = NULL; + } } else if (participant->can_be_key_server) { i_is_key_server = TRUE; } @@ -2280,6 +2340,16 @@ ieee802_1x_participant_send_mkpdu( static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa); + +static void ieee802_1x_delete_transmit_sa(struct ieee802_1x_kay *kay, + struct transmit_sa *sa) +{ + secy_disable_transmit_sa(kay, sa); + secy_delete_transmit_sa(kay, sa); + ieee802_1x_kay_deinit_transmit_sa(sa); +} + + /** * ieee802_1x_participant_timer - */ @@ -2323,7 +2393,6 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) &participant->rxsc_list, struct receive_sc, list) { if (sci_equal(&rxsc->sci, &peer->sci)) { - secy_delete_receive_sc(kay, rxsc); ieee802_1x_kay_deinit_receive_sc( participant, rxsc); } @@ -2340,7 +2409,13 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) participant->advised_capability = MACSEC_CAP_NOT_IMPLEMENTED; participant->to_use_sak = FALSE; - kay->authenticated = TRUE; + participant->ltx = FALSE; + participant->lrx = FALSE; + participant->otx = FALSE; + participant->orx = FALSE; + participant->is_key_server = FALSE; + participant->is_elected = FALSE; + kay->authenticated = FALSE; kay->secured = FALSE; kay->failed = FALSE; kay->ltx_kn = 0; @@ -2354,11 +2429,10 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) dl_list_for_each_safe(txsa, pre_txsa, &participant->txsc->sa_list, struct transmit_sa, list) { - secy_disable_transmit_sa(kay, txsa); - ieee802_1x_kay_deinit_transmit_sa(txsa); + ieee802_1x_delete_transmit_sa(kay, txsa); } - ieee802_1x_cp_connect_authenticated(kay->cp); + ieee802_1x_cp_connect_pending(kay->cp); ieee802_1x_cp_sm_step(kay->cp); } else { ieee802_1x_kay_elect_key_server(participant); @@ -2385,7 +2459,8 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) participant->new_sak = FALSE; } - if (participant->retry_count < MAX_RETRY_CNT) { + if (participant->retry_count < MAX_RETRY_CNT || + participant->mode == PSK) { ieee802_1x_participant_send_mkpdu(participant); participant->retry_count++; } @@ -2429,6 +2504,7 @@ ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN, psa->confidentiality = FALSE; psa->an = an; + ieee802_1x_kay_use_data_key(key); psa->pkey = key; psa->next_pn = next_PN; psa->sc = psc; @@ -2438,8 +2514,8 @@ ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN, dl_list_add(&psc->sa_list, &psa->list); wpa_printf(MSG_DEBUG, - "KaY: Create transmit SA(an: %hhu, next_PN: %u) of SC(channel: %d)", - an, next_PN, psc->channel); + "KaY: Create transmit SA(an: %hhu, next_PN: %u) of SC", + an, next_PN); return psa; } @@ -2450,6 +2526,7 @@ ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN, */ static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa) { + ieee802_1x_kay_deinit_data_key(psa->pkey); psa->pkey = NULL; wpa_printf(MSG_DEBUG, "KaY: Delete transmit SA(an: %hhu) of SC", @@ -2463,8 +2540,7 @@ static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa) * init_transmit_sc - */ static struct transmit_sc * -ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci, - int channel) +ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci) { struct transmit_sc *psc; @@ -2474,7 +2550,6 @@ ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci, return NULL; } os_memcpy(&psc->sci, sci, sizeof(psc->sci)); - psc->channel = channel; os_get_time(&psc->created_time); psc->transmitting = FALSE; @@ -2482,7 +2557,7 @@ ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci, psc->enciphering_sa = FALSE; dl_list_init(&psc->sa_list); - wpa_printf(MSG_DEBUG, "KaY: Create transmit SC(channel: %d)", channel); + wpa_printf(MSG_DEBUG, "KaY: Create transmit SC"); wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)sci , sizeof(*sci)); return psc; @@ -2498,14 +2573,11 @@ ieee802_1x_kay_deinit_transmit_sc( { struct transmit_sa *psa, *tmp; - wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC(channel: %d)", - psc->channel); - dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa, - list) { - secy_disable_transmit_sa(participant->kay, psa); - ieee802_1x_kay_deinit_transmit_sa(psa); - } + wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC"); + dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa, list) + ieee802_1x_delete_transmit_sa(participant->kay, psa); + secy_delete_transmit_sc(participant->kay, psc); os_free(psc); } @@ -2582,6 +2654,32 @@ int ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay *kay, } +static struct transmit_sa * lookup_txsa_by_an(struct transmit_sc *txsc, u8 an) +{ + struct transmit_sa *txsa; + + dl_list_for_each(txsa, &txsc->sa_list, struct transmit_sa, list) { + if (txsa->an == an) + return txsa; + } + + return NULL; +} + + +static struct receive_sa * lookup_rxsa_by_an(struct receive_sc *rxsc, u8 an) +{ + struct receive_sa *rxsa; + + dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list) { + if (rxsa->an == an) + return rxsa; + } + + return NULL; +} + + /** * ieee802_1x_kay_create_sas - */ @@ -2616,6 +2714,9 @@ int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay, } dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) { + while ((rxsa = lookup_rxsa_by_an(rxsc, latest_sak->an)) != NULL) + ieee802_1x_delete_receive_sa(kay, rxsa); + rxsa = ieee802_1x_kay_init_receive_sa(rxsc, latest_sak->an, 1, latest_sak); if (!rxsa) @@ -2624,6 +2725,10 @@ int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay, secy_create_receive_sa(kay, rxsa); } + while ((txsa = lookup_txsa_by_an(principal->txsc, latest_sak->an)) != + NULL) + ieee802_1x_delete_transmit_sa(kay, txsa); + txsa = ieee802_1x_kay_init_transmit_sa(principal->txsc, latest_sak->an, 1, latest_sak); if (!txsa) @@ -2657,20 +2762,16 @@ int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay, /* remove the transmit sa */ dl_list_for_each_safe(txsa, pre_txsa, &principal->txsc->sa_list, struct transmit_sa, list) { - if (is_ki_equal(&txsa->pkey->key_identifier, ki)) { - secy_disable_transmit_sa(kay, txsa); - ieee802_1x_kay_deinit_transmit_sa(txsa); - } + if (is_ki_equal(&txsa->pkey->key_identifier, ki)) + ieee802_1x_delete_transmit_sa(kay, txsa); } /* remove the receive sa */ dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) { dl_list_for_each_safe(rxsa, pre_rxsa, &rxsc->sa_list, struct receive_sa, list) { - if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) { - secy_disable_receive_sa(kay, rxsa); - ieee802_1x_kay_deinit_receive_sa(rxsa); - } + if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) + ieee802_1x_delete_receive_sa(kay, rxsa); } } @@ -2678,6 +2779,7 @@ int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay, dl_list_for_each_safe(sa_key, pre_key, &principal->sak_list, struct data_key, list) { if (is_ki_equal(&sa_key->key_identifier, ki)) { + dl_list_del(&sa_key->list); ieee802_1x_kay_deinit_data_key(sa_key); break; } @@ -2759,7 +2861,7 @@ int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay) if (!principal) return -1; - if (principal->retry_count < MAX_RETRY_CNT) { + if (principal->retry_count < MAX_RETRY_CNT || principal->mode == PSK) { ieee802_1x_participant_send_mkpdu(principal); principal->retry_count++; } @@ -2782,6 +2884,7 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, size_t mka_msg_len; struct ieee802_1x_mka_participant *participant; size_t body_len; + size_t ckn_len; u8 icv[MAX_ICV_LEN]; u8 *msg_icv; @@ -2821,8 +2924,22 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, return -1; } + if (body_len < sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN) { + wpa_printf(MSG_DEBUG, "KaY: Too small body length %zu", + body_len); + return -1; + } + ckn_len = body_len - + (sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN); + if (ckn_len < 1 || ckn_len > MAX_CKN_LEN) { + wpa_printf(MSG_ERROR, + "KaY: Received EAPOL-MKA CKN Length (%zu bytes) is out of range (<= %u bytes)", + ckn_len, MAX_CKN_LEN); + return -1; + } + /* CKN should be owned by I */ - participant = ieee802_1x_kay_get_participant(kay, body->ckn); + participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len); if (!participant) { wpa_printf(MSG_DEBUG, "CKN is not included in my CA"); return -1; @@ -2945,7 +3062,7 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, "KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV", left_len, MKA_HDR_LEN, body_len, DEFAULT_ICV_LEN); - continue; + return -1; } if (handled[body_type]) @@ -3020,13 +3137,14 @@ static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf, */ struct ieee802_1x_kay * ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, - const char *ifname, const u8 *addr) + u16 port, u8 priority, const char *ifname, const u8 *addr) { struct ieee802_1x_kay *kay; kay = os_zalloc(sizeof(*kay)); if (!kay) { wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__); + os_free(ctx); return NULL; } @@ -3042,8 +3160,8 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, os_strlcpy(kay->if_name, ifname, IFNAMSIZ); os_memcpy(kay->actor_sci.addr, addr, ETH_ALEN); - kay->actor_sci.port = host_to_be16(0x0001); - kay->actor_priority = DEFAULT_PRIO_NOT_KEY_SERVER; + kay->actor_sci.port = host_to_be16(port ? port : 0x0001); + kay->actor_priority = priority; /* While actor acts as a key server, shall distribute sakey */ kay->dist_kn = 1; @@ -3060,7 +3178,12 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, dl_list_init(&kay->participant_list); - if (policy == DO_NOT_SECURE) { + if (policy != DO_NOT_SECURE && + secy_get_capability(kay, &kay->macsec_capable) < 0) + goto error; + + if (policy == DO_NOT_SECURE || + kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) { kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED; kay->macsec_desired = FALSE; kay->macsec_protect = FALSE; @@ -3069,29 +3192,32 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, kay->macsec_replay_window = 0; kay->macsec_confidentiality = CONFIDENTIALITY_NONE; } else { - kay->macsec_capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50; kay->macsec_desired = TRUE; kay->macsec_protect = TRUE; + kay->macsec_encrypt = policy == SHOULD_ENCRYPT; kay->macsec_validate = Strict; kay->macsec_replay_protect = FALSE; kay->macsec_replay_window = 0; - kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0; + if (kay->macsec_capable >= MACSEC_CAP_INTEG_AND_CONF) + kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0; + else + kay->macsec_confidentiality = CONFIDENTIALITY_NONE; } wpa_printf(MSG_DEBUG, "KaY: state machine created"); /* Initialize the SecY must be prio to CP, as CP will control SecY */ - secy_init_macsec(kay); - secy_get_available_transmit_sc(kay, &kay->sc_ch); + if (secy_init_macsec(kay) < 0) { + wpa_printf(MSG_DEBUG, "KaY: Could not initialize MACsec"); + goto error; + } wpa_printf(MSG_DEBUG, "KaY: secy init macsec done"); /* init CP */ kay->cp = ieee802_1x_cp_sm_init(kay); - if (kay->cp == NULL) { - ieee802_1x_kay_deinit(kay); - return NULL; - } + if (kay->cp == NULL) + goto error; if (policy == DO_NOT_SECURE) { ieee802_1x_cp_connect_authenticated(kay->cp); @@ -3102,12 +3228,15 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, if (kay->l2_mka == NULL) { wpa_printf(MSG_WARNING, "KaY: Failed to initialize L2 packet processing for MKA packet"); - ieee802_1x_kay_deinit(kay); - return NULL; + goto error; } } return kay; + +error: + ieee802_1x_kay_deinit(kay); + return NULL; } @@ -3148,8 +3277,9 @@ ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay) * ieee802_1x_kay_create_mka - */ struct ieee802_1x_mka_participant * -ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn, - struct mka_key *cak, u32 life, +ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, + const struct mka_key_name *ckn, + const struct mka_key *cak, u32 life, enum mka_created_mode mode, Boolean is_authenticator) { struct ieee802_1x_mka_participant *participant; @@ -3243,8 +3373,7 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn, dl_list_init(&participant->sak_list); participant->new_key = NULL; dl_list_init(&participant->rxsc_list); - participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci, - kay->sc_ch); + participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci); secy_cp_control_protect_frames(kay, kay->macsec_protect); secy_cp_control_replay(kay, kay->macsec_replay_protect, kay->macsec_replay_window); @@ -3281,8 +3410,17 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn, usecs = os_random() % (MKA_HELLO_TIME * 1000); eloop_register_timeout(0, usecs, ieee802_1x_participant_timer, participant, NULL); - participant->mka_life = MKA_LIFE_TIME / 1000 + time(NULL) + - usecs / 1000000; + + /* Disable MKA lifetime for PSK mode. + * The peer(s) can take a long time to come up, because we + * create a "standby" MKA, and we need it to remain live until + * some peer appears. + */ + if (mode != PSK) { + participant->mka_life = MKA_LIFE_TIME / 1000 + time(NULL) + + usecs / 1000000; + } + participant->mode = mode; return participant; @@ -3309,7 +3447,7 @@ ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn) wpa_printf(MSG_DEBUG, "KaY: participant removed"); /* get the participant */ - participant = ieee802_1x_kay_get_participant(kay, ckn->name); + participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len); if (!participant) { wpa_hexdump(MSG_DEBUG, "KaY: participant is not found", ckn->name, ckn->len); @@ -3340,16 +3478,13 @@ ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn) sak = dl_list_entry(participant->sak_list.next, struct data_key, list); dl_list_del(&sak->list); - os_free(sak->key); - os_free(sak); + ieee802_1x_kay_deinit_data_key(sak); } while (!dl_list_empty(&participant->rxsc_list)) { rxsc = dl_list_entry(participant->rxsc_list.next, struct receive_sc, list); - secy_delete_receive_sc(kay, rxsc); ieee802_1x_kay_deinit_receive_sc(participant, rxsc); } - secy_delete_transmit_sc(kay, participant->txsc); ieee802_1x_kay_deinit_transmit_sc(participant, participant->txsc); os_memset(&participant->cak, 0, sizeof(participant->cak)); @@ -3371,7 +3506,7 @@ void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay, if (!kay || !ckn) return; - participant = ieee802_1x_kay_get_participant(kay, ckn->name); + participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len); if (!participant) return; @@ -3409,6 +3544,7 @@ ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, unsigned int cs_index) { struct ieee802_1x_mka_participant *participant; + enum macsec_cap secy_cap; if (!kay) return -1; @@ -3427,6 +3563,12 @@ ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, kay->macsec_csindex = cs_index; kay->macsec_capable = cipher_suite_tbl[kay->macsec_csindex].capable; + if (secy_get_capability(kay, &secy_cap) < 0) + return -3; + + if (kay->macsec_capable > secy_cap) + kay->macsec_capable = secy_cap; + participant = ieee802_1x_kay_get_principal_participant(kay); if (participant) { wpa_printf(MSG_INFO, "KaY: Cipher Suite changed"); @@ -3435,3 +3577,51 @@ ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, return 0; } + + +#ifdef CONFIG_CTRL_IFACE +/** + * ieee802_1x_kay_get_status - Get IEEE 802.1X KaY status details + * @sm: Pointer to KaY allocated with ieee802_1x_kay_init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + * + * Query KAY status information. This function fills in a text area with current + * status information. If the buffer (buf) is not large enough, status + * information will be truncated to fit the buffer. + */ +int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf, + size_t buflen) +{ + int len; + + if (!kay) + return 0; + + len = os_snprintf(buf, buflen, + "PAE KaY status=%s\n" + "Authenticated=%s\n" + "Secured=%s\n" + "Failed=%s\n" + "Actor Priority=%u\n" + "Key Server Priority=%u\n" + "Is Key Server=%s\n" + "Number of Keys Distributed=%u\n" + "Number of Keys Received=%u\n", + kay->active ? "Active" : "Not-Active", + kay->authenticated ? "Yes" : "No", + kay->secured ? "Yes" : "No", + kay->failed ? "Yes" : "No", + kay->actor_priority, + kay->key_server_priority, + kay->is_key_server ? "Yes" : "No", + kay->dist_kn - 1, + kay->rcvd_keys); + if (os_snprintf_error(buflen, len)) + return 0; + + return len; +} +#endif /* CONFIG_CTRL_IFACE */ diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h index afbaa336cbda..b2650596cae3 100644 --- a/src/pae/ieee802_1x_kay.h +++ b/src/pae/ieee802_1x_kay.h @@ -15,7 +15,7 @@ struct macsec_init_params; -#define MI_LEN 12 +#define MI_LEN 12 /* 96-bit Member Identifier */ #define MAX_KEY_LEN 32 /* 32 bytes, 256 bits */ #define MAX_CKN_LEN 32 /* 32 bytes, 256 bits */ @@ -24,6 +24,12 @@ struct macsec_init_params; #define MKA_LIFE_TIME 6000 #define MKA_SAK_RETIRE_TIME 3000 +/** + * struct ieee802_1x_mka_ki - Key Identifier (KI) + * @mi: Key Server's Member Identifier + * @kn: Key Number, assigned by the Key Server + * IEEE 802.1X-2010 9.8 SAK generation, distribution, and selection + */ struct ieee802_1x_mka_ki { u8 mi[MI_LEN]; u32 kn; @@ -49,6 +55,84 @@ enum mka_created_mode { EAP_EXCHANGE, }; +struct data_key { + u8 *key; + int key_len; + struct ieee802_1x_mka_ki key_identifier; + enum confidentiality_offset confidentiality_offset; + u8 an; + Boolean transmits; + Boolean receives; + struct os_time created_time; + u32 next_pn; + + /* not defined data */ + Boolean rx_latest; + Boolean tx_latest; + + int user; + + struct dl_list list; +}; + +/* TransmitSC in IEEE Std 802.1AE-2006, Figure 10-6 */ +struct transmit_sc { + struct ieee802_1x_mka_sci sci; /* const SCI sci */ + Boolean transmitting; /* bool transmitting (read only) */ + + struct os_time created_time; /* Time createdTime */ + + u8 encoding_sa; /* AN encodingSA (read only) */ + u8 enciphering_sa; /* AN encipheringSA (read only) */ + + /* not defined data */ + struct dl_list list; + struct dl_list sa_list; +}; + +/* TransmitSA in IEEE Std 802.1AE-2006, Figure 10-6 */ +struct transmit_sa { + Boolean in_use; /* bool inUse (read only) */ + u32 next_pn; /* PN nextPN (read only) */ + struct os_time created_time; /* Time createdTime */ + + Boolean enable_transmit; /* bool EnableTransmit */ + + u8 an; + Boolean confidentiality; + struct data_key *pkey; + + struct transmit_sc *sc; + struct dl_list list; /* list entry in struct transmit_sc::sa_list */ +}; + +/* ReceiveSC in IEEE Std 802.1AE-2006, Figure 10-6 */ +struct receive_sc { + struct ieee802_1x_mka_sci sci; /* const SCI sci */ + Boolean receiving; /* bool receiving (read only) */ + + struct os_time created_time; /* Time createdTime */ + + struct dl_list list; + struct dl_list sa_list; +}; + +/* ReceiveSA in IEEE Std 802.1AE-2006, Figure 10-6 */ +struct receive_sa { + Boolean enable_receive; /* bool enableReceive */ + Boolean in_use; /* bool inUse (read only) */ + + u32 next_pn; /* PN nextPN (read only) */ + u32 lowest_pn; /* PN lowestPN (read only) */ + u8 an; + struct os_time created_time; + + struct data_key *pkey; + struct receive_sc *sc; /* list entry in struct receive_sc::sa_list */ + + struct dl_list list; +}; + struct ieee802_1x_kay_ctx { /* pointer to arbitrary upper level context */ void *ctx; @@ -56,34 +140,30 @@ struct ieee802_1x_kay_ctx { /* abstract wpa driver interface */ int (*macsec_init)(void *ctx, struct macsec_init_params *params); int (*macsec_deinit)(void *ctx); + int (*macsec_get_capability)(void *priv, enum macsec_cap *cap); int (*enable_protect_frames)(void *ctx, Boolean enabled); + int (*enable_encrypt)(void *ctx, Boolean enabled); int (*set_replay_protect)(void *ctx, Boolean enabled, u32 window); int (*set_current_cipher_suite)(void *ctx, u64 cs); int (*enable_controlled_port)(void *ctx, Boolean enabled); - int (*get_receive_lowest_pn)(void *ctx, u32 channel, u8 an, - u32 *lowest_pn); - int (*get_transmit_next_pn)(void *ctx, u32 channel, u8 an, - u32 *next_pn); - int (*set_transmit_next_pn)(void *ctx, u32 channel, u8 an, u32 next_pn); - int (*get_available_receive_sc)(void *ctx, u32 *channel); - int (*create_receive_sc)(void *ctx, u32 channel, - struct ieee802_1x_mka_sci *sci, + int (*get_receive_lowest_pn)(void *ctx, struct receive_sa *sa); + int (*get_transmit_next_pn)(void *ctx, struct transmit_sa *sa); + int (*set_transmit_next_pn)(void *ctx, struct transmit_sa *sa); + int (*create_receive_sc)(void *ctx, struct receive_sc *sc, enum validate_frames vf, enum confidentiality_offset co); - int (*delete_receive_sc)(void *ctx, u32 channel); - int (*create_receive_sa)(void *ctx, u32 channel, u8 an, u32 lowest_pn, - const u8 *sak); - int (*enable_receive_sa)(void *ctx, u32 channel, u8 an); - int (*disable_receive_sa)(void *ctx, u32 channel, u8 an); - int (*get_available_transmit_sc)(void *ctx, u32 *channel); - int (*create_transmit_sc)(void *ctx, u32 channel, - const struct ieee802_1x_mka_sci *sci, + int (*delete_receive_sc)(void *ctx, struct receive_sc *sc); + int (*create_receive_sa)(void *ctx, struct receive_sa *sa); + int (*delete_receive_sa)(void *ctx, struct receive_sa *sa); + int (*enable_receive_sa)(void *ctx, struct receive_sa *sa); + int (*disable_receive_sa)(void *ctx, struct receive_sa *sa); + int (*create_transmit_sc)(void *ctx, struct transmit_sc *sc, enum confidentiality_offset co); - int (*delete_transmit_sc)(void *ctx, u32 channel); - int (*create_transmit_sa)(void *ctx, u32 channel, u8 an, u32 next_pn, - Boolean confidentiality, const u8 *sak); - int (*enable_transmit_sa)(void *ctx, u32 channel, u8 an); - int (*disable_transmit_sa)(void *ctx, u32 channel, u8 an); + int (*delete_transmit_sc)(void *ctx, struct transmit_sc *sc); + int (*create_transmit_sa)(void *ctx, struct transmit_sa *sa); + int (*delete_transmit_sa)(void *ctx, struct transmit_sa *sa); + int (*enable_transmit_sa)(void *ctx, struct transmit_sa *sa); + int (*disable_transmit_sa)(void *ctx, struct transmit_sa *sa); }; struct ieee802_1x_kay { @@ -102,6 +182,7 @@ struct ieee802_1x_kay { enum macsec_cap macsec_capable; Boolean macsec_desired; Boolean macsec_protect; + Boolean macsec_encrypt; Boolean macsec_replay_protect; u32 macsec_replay_window; enum validate_frames macsec_validate; @@ -127,12 +208,12 @@ struct ieee802_1x_kay { int mka_algindex; /* MKA alg table index */ u32 dist_kn; + u32 rcvd_keys; u8 dist_an; time_t dist_time; u8 mka_version; u8 algo_agility[4]; - u32 sc_ch; u32 pn_exhaustion; Boolean port_enable; @@ -151,14 +232,17 @@ struct ieee802_1x_kay { }; +u64 mka_sci_u64(struct ieee802_1x_mka_sci *sci); + struct ieee802_1x_kay * ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, - const char *ifname, const u8 *addr); + u16 port, u8 priority, const char *ifname, const u8 *addr); void ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay); struct ieee802_1x_mka_participant * ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, - struct mka_key_name *ckn, struct mka_key *cak, + const struct mka_key_name *ckn, + const struct mka_key *cak, u32 life, enum mka_created_mode mode, Boolean is_authenticator); void ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, @@ -185,5 +269,7 @@ int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay, int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay, struct ieee802_1x_mka_ki *lki); int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay); +int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf, + size_t buflen); #endif /* IEEE802_1X_KAY_H */ diff --git a/src/pae/ieee802_1x_kay_i.h b/src/pae/ieee802_1x_kay_i.h index 622282e97c51..bc522d89852b 100644 --- a/src/pae/ieee802_1x_kay_i.h +++ b/src/pae/ieee802_1x_kay_i.h @@ -54,88 +54,6 @@ struct ieee802_1x_kay_peer { struct dl_list list; }; -struct data_key { - u8 *key; - int key_len; - struct ieee802_1x_mka_ki key_identifier; - enum confidentiality_offset confidentiality_offset; - u8 an; - Boolean transmits; - Boolean receives; - struct os_time created_time; - u32 next_pn; - - /* not defined data */ - Boolean rx_latest; - Boolean tx_latest; - - int user; /* FIXME: to indicate if it can be delete safely */ - - struct dl_list list; -}; - -/* TransmitSC in IEEE Std 802.1AE-2006, Figure 10-6 */ -struct transmit_sc { - struct ieee802_1x_mka_sci sci; /* const SCI sci */ - Boolean transmitting; /* bool transmitting (read only) */ - - struct os_time created_time; /* Time createdTime */ - - u8 encoding_sa; /* AN encodingSA (read only) */ - u8 enciphering_sa; /* AN encipheringSA (read only) */ - - /* not defined data */ - unsigned int channel; - - struct dl_list list; - struct dl_list sa_list; -}; - -/* TransmitSA in IEEE Std 802.1AE-2006, Figure 10-6 */ -struct transmit_sa { - Boolean in_use; /* bool inUse (read only) */ - u32 next_pn; /* PN nextPN (read only) */ - struct os_time created_time; /* Time createdTime */ - - Boolean enable_transmit; /* bool EnableTransmit */ - - u8 an; - Boolean confidentiality; - struct data_key *pkey; - - struct transmit_sc *sc; - struct dl_list list; /* list entry in struct transmit_sc::sa_list */ -}; - -/* ReceiveSC in IEEE Std 802.1AE-2006, Figure 10-6 */ -struct receive_sc { - struct ieee802_1x_mka_sci sci; /* const SCI sci */ - Boolean receiving; /* bool receiving (read only) */ - - struct os_time created_time; /* Time createdTime */ - - unsigned int channel; - - struct dl_list list; - struct dl_list sa_list; -}; - -/* ReceiveSA in IEEE Std 802.1AE-2006, Figure 10-6 */ -struct receive_sa { - Boolean enable_receive; /* bool enableReceive */ - Boolean in_use; /* bool inUse (read only) */ - - u32 next_pn; /* PN nextPN (read only) */ - u32 lowest_pn; /* PN lowestPN (read only) */ - u8 an; - struct os_time created_time; - - struct data_key *pkey; - struct receive_sc *sc; /* list entry in struct receive_sc::sa_list */ - - struct dl_list list; -}; - struct macsec_ciphersuite { u64 id; char name[32]; @@ -175,6 +93,7 @@ struct ieee802_1x_mka_participant { Boolean active; Boolean participant; Boolean retain; + enum mka_created_mode mode; enum { DEFAULT, DISABLED, ON_OPER_UP, ALWAYS } activate; @@ -250,6 +169,22 @@ struct ieee802_1x_mka_hdr { #define MKA_HDR_LEN sizeof(struct ieee802_1x_mka_hdr) +/** + * struct ieee802_1x_mka_basic_body - Basic Parameter Set (Figure 11-8) + * @version: MKA Version Identifier + * @priority: Key Server Priority + * @length: Parameter set body length + * @macsec_capability: MACsec capability, as defined in ieee802_1x_defs.h + * @macsec_desired: the participant wants MACsec to be used to protect frames + * (9.6.1) + * @key_server: the participant has not decided that another participant is or + * will be the key server (9.5.1) + * @length1: Parameter set body length (cont) + * @actor_mi: Actor's Member Identifier + * @actor_mn: Actor's Message Number + * @algo_agility: Algorithm Agility parameter + * @ckn: CAK Name + */ struct ieee802_1x_mka_basic_body { /* octet 1 */ u8 version; @@ -279,6 +214,14 @@ struct ieee802_1x_mka_basic_body { u8 ckn[0]; }; +/** + * struct ieee802_1x_mka_peer_body - Live Peer List and Potential Peer List + * parameter sets (Figure 11-9) + * @type: Parameter set type (1 or 2) + * @length: Parameter set body length + * @length1: Parameter set body length (cont) + * @peer: array of (MI, MN) pairs + */ struct ieee802_1x_mka_peer_body { /* octet 1 */ u8 type; @@ -299,6 +242,28 @@ struct ieee802_1x_mka_peer_body { /* followed by Peers */ }; +/** + * struct ieee802_1x_mka_sak_use_body - MACsec SAK Use parameter set (Figure + * 11-10) + * @type: MKA message type + * @lan: latest key AN + * @ltx: latest key TX + * @lrx: latest key RX + * @oan: old key AN + * @otx: old key TX + * @orx: old key RX + * @ptx: plain TX, ie protectFrames is False + * @prx: plain RX, ie validateFrames is not Strict + * @delay_protect: True if LPNs are being reported sufficiently frequently to + * allow the recipient to provide data delay protection. If False, the LPN + * can be reported as zero. + * @lsrv_mi: latest key server MI + * @lkn: latest key number (together with MI, form the KI) + * @llpn: latest lowest acceptable PN (LPN) + * @osrv_mi: old key server MI + * @okn: old key number (together with MI, form the KI) + * @olpn: old lowest acceptable PN (LPN) + */ struct ieee802_1x_mka_sak_use_body { /* octet 1 */ u8 type; @@ -352,7 +317,21 @@ struct ieee802_1x_mka_sak_use_body { be32 olpn; }; - +/** + * struct ieee802_1x_mka_dist_sak_body - Distributed SAK parameter set + * (GCM-AES-128, Figure 11-11) + * @type: Parameter set type (4) + * @length: Parameter set body length + * @length1: Parameter set body length (cont) + * Total parameter body length values: + * - 0 for plain text + * - 28 for GCM-AES-128 + * - 36 or more for other cipher suites + * @confid_offset: confidentiality offset, as defined in ieee802_1x_defs.h + * @dan: distributed AN (0 for plain text) + * @kn: Key Number + * @sak: AES Key Wrap of SAK (see 9.8) + */ struct ieee802_1x_mka_dist_sak_body { /* octet 1 */ u8 type; @@ -385,6 +364,41 @@ struct ieee802_1x_mka_dist_sak_body { u8 sak[0]; }; +/** + * struct ieee802_1x_mka_dist_cak_body - Distributed CAK parameter set (Figure + * 11-13) + * @type: Parameter set type (5) + * @length: Parameter set body length + * @length1: Parameter set body length (cont) + * Total parameter body length values: + * - 0 for plain text + * - 28 for GCM-AES-128 + * - 36 or more for other cipher suites + * @cak: AES Key Wrap of CAK (see 9.8) + * @ckn: CAK Name + */ +struct ieee802_1x_mka_dist_cak_body { + /* octet 1 */ + u8 type; + /* octet 2 */ + u8 reserve; + /* octet 3 */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + u8 length:4; + u8 reserve1:4; +#elif __BYTE_ORDER == __BIG_ENDIAN + u8 reserve1:4; + u8 length:4; +#endif + /* octet 4 */ + u8 length1; + + /* octet 5 - 28 */ + u8 cak[24]; + + /* followed by CAK Name, 29- */ + u8 ckn[0]; +}; struct ieee802_1x_mka_icv_body { /* octet 1 */ diff --git a/src/pae/ieee802_1x_secy_ops.c b/src/pae/ieee802_1x_secy_ops.c index 2d12911dbfcf..ab5339bb2046 100644 --- a/src/pae/ieee802_1x_secy_ops.c +++ b/src/pae/ieee802_1x_secy_ops.c @@ -45,6 +45,26 @@ int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, Boolean enabled) } +int secy_cp_control_encrypt(struct ieee802_1x_kay *kay, Boolean enabled) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->enable_encrypt) { + wpa_printf(MSG_ERROR, + "KaY: secy enable_encrypt operation not supported"); + return -1; + } + + return ops->enable_encrypt(ops->ctx, enabled); +} + + int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean enabled, u32 win) { struct ieee802_1x_kay_ctx *ops; @@ -113,55 +133,48 @@ int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, Boolean enabled) } -int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay, - struct receive_sa *rxsa) +int secy_get_capability(struct ieee802_1x_kay *kay, enum macsec_cap *cap) { struct ieee802_1x_kay_ctx *ops; - if (!kay || !rxsa) { + if (!kay) { wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); return -1; } ops = kay->ctx; - if (!ops || !ops->get_receive_lowest_pn) { + if (!ops || !ops->macsec_get_capability) { wpa_printf(MSG_ERROR, - "KaY: secy get_receive_lowest_pn operation not supported"); + "KaY: secy macsec_get_capability operation not supported"); return -1; } - return ops->get_receive_lowest_pn(ops->ctx, - rxsa->sc->channel, - rxsa->an, - &rxsa->lowest_pn); + return ops->macsec_get_capability(ops->ctx, cap); } -int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay, - struct transmit_sa *txsa) +int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay, + struct receive_sa *rxsa) { struct ieee802_1x_kay_ctx *ops; - if (!kay || !txsa) { + if (!kay || !rxsa) { wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); return -1; } ops = kay->ctx; - if (!ops || !ops->get_transmit_next_pn) { + if (!ops || !ops->get_receive_lowest_pn) { wpa_printf(MSG_ERROR, "KaY: secy get_receive_lowest_pn operation not supported"); return -1; } - return ops->get_transmit_next_pn(ops->ctx, - txsa->sc->channel, - txsa->an, - &txsa->next_pn); + return ops->get_receive_lowest_pn(ops->ctx, rxsa); } -int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay, +int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay, struct transmit_sa *txsa) { struct ieee802_1x_kay_ctx *ops; @@ -172,36 +185,34 @@ int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay, } ops = kay->ctx; - if (!ops || !ops->set_transmit_next_pn) { + if (!ops || !ops->get_transmit_next_pn) { wpa_printf(MSG_ERROR, "KaY: secy get_receive_lowest_pn operation not supported"); return -1; } - return ops->set_transmit_next_pn(ops->ctx, - txsa->sc->channel, - txsa->an, - txsa->next_pn); + return ops->get_transmit_next_pn(ops->ctx, txsa); } -int secy_get_available_receive_sc(struct ieee802_1x_kay *kay, u32 *channel) +int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay, + struct transmit_sa *txsa) { struct ieee802_1x_kay_ctx *ops; - if (!kay) { + if (!kay || !txsa) { wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); return -1; } ops = kay->ctx; - if (!ops || !ops->get_available_receive_sc) { + if (!ops || !ops->set_transmit_next_pn) { wpa_printf(MSG_ERROR, - "KaY: secy get_available_receive_sc operation not supported"); + "KaY: secy get_receive_lowest_pn operation not supported"); return -1; } - return ops->get_available_receive_sc(ops->ctx, channel); + return ops->set_transmit_next_pn(ops->ctx, txsa); } @@ -221,8 +232,7 @@ int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc) return -1; } - return ops->create_receive_sc(ops->ctx, rxsc->channel, &rxsc->sci, - kay->vf, kay->co); + return ops->create_receive_sc(ops->ctx, rxsc, kay->vf, kay->co); } @@ -242,7 +252,7 @@ int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc) return -1; } - return ops->delete_receive_sc(ops->ctx, rxsc->channel); + return ops->delete_receive_sc(ops->ctx, rxsc); } @@ -262,12 +272,11 @@ int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) return -1; } - return ops->create_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an, - rxsa->lowest_pn, rxsa->pkey->key); + return ops->create_receive_sa(ops->ctx, rxsa); } -int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) +int secy_delete_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) { struct ieee802_1x_kay_ctx *ops; @@ -277,19 +286,17 @@ int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) } ops = kay->ctx; - if (!ops || !ops->enable_receive_sa) { + if (!ops || !ops->delete_receive_sa) { wpa_printf(MSG_ERROR, - "KaY: secy enable_receive_sa operation not supported"); + "KaY: secy delete_receive_sa operation not supported"); return -1; } - rxsa->enable_receive = TRUE; - - return ops->enable_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an); + return ops->delete_receive_sa(ops->ctx, rxsa); } -int secy_disable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) +int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) { struct ieee802_1x_kay_ctx *ops; @@ -299,35 +306,37 @@ int secy_disable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) } ops = kay->ctx; - if (!ops || !ops->disable_receive_sa) { + if (!ops || !ops->enable_receive_sa) { wpa_printf(MSG_ERROR, - "KaY: secy disable_receive_sa operation not supported"); + "KaY: secy enable_receive_sa operation not supported"); return -1; } - rxsa->enable_receive = FALSE; + rxsa->enable_receive = TRUE; - return ops->disable_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an); + return ops->enable_receive_sa(ops->ctx, rxsa); } -int secy_get_available_transmit_sc(struct ieee802_1x_kay *kay, u32 *channel) +int secy_disable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) { struct ieee802_1x_kay_ctx *ops; - if (!kay) { + if (!kay || !rxsa) { wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); return -1; } ops = kay->ctx; - if (!ops || !ops->get_available_transmit_sc) { + if (!ops || !ops->disable_receive_sa) { wpa_printf(MSG_ERROR, - "KaY: secy get_available_transmit_sc operation not supported"); + "KaY: secy disable_receive_sa operation not supported"); return -1; } - return ops->get_available_transmit_sc(ops->ctx, channel); + rxsa->enable_receive = FALSE; + + return ops->disable_receive_sa(ops->ctx, rxsa); } @@ -348,8 +357,7 @@ int secy_create_transmit_sc(struct ieee802_1x_kay *kay, return -1; } - return ops->create_transmit_sc(ops->ctx, txsc->channel, &txsc->sci, - kay->co); + return ops->create_transmit_sc(ops->ctx, txsc, kay->co); } @@ -370,7 +378,7 @@ int secy_delete_transmit_sc(struct ieee802_1x_kay *kay, return -1; } - return ops->delete_transmit_sc(ops->ctx, txsc->channel); + return ops->delete_transmit_sc(ops->ctx, txsc); } @@ -391,9 +399,28 @@ int secy_create_transmit_sa(struct ieee802_1x_kay *kay, return -1; } - return ops->create_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an, - txsa->next_pn, txsa->confidentiality, - txsa->pkey->key); + return ops->create_transmit_sa(ops->ctx, txsa); +} + + +int secy_delete_transmit_sa(struct ieee802_1x_kay *kay, + struct transmit_sa *txsa) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay || !txsa) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->delete_transmit_sa) { + wpa_printf(MSG_ERROR, + "KaY: secy delete_transmit_sa operation not supported"); + return -1; + } + + return ops->delete_transmit_sa(ops->ctx, txsa); } @@ -416,7 +443,7 @@ int secy_enable_transmit_sa(struct ieee802_1x_kay *kay, txsa->enable_transmit = TRUE; - return ops->enable_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an); + return ops->enable_transmit_sa(ops->ctx, txsa); } @@ -439,7 +466,7 @@ int secy_disable_transmit_sa(struct ieee802_1x_kay *kay, txsa->enable_transmit = FALSE; - return ops->disable_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an); + return ops->disable_transmit_sa(ops->ctx, txsa); } diff --git a/src/pae/ieee802_1x_secy_ops.h b/src/pae/ieee802_1x_secy_ops.h index f5057ee11958..9fb29c3ddfa0 100644 --- a/src/pae/ieee802_1x_secy_ops.h +++ b/src/pae/ieee802_1x_secy_ops.h @@ -13,10 +13,6 @@ #include "common/ieee802_1x_defs.h" struct ieee802_1x_kay_conf; -struct receive_sa; -struct transmit_sa; -struct receive_sc; -struct transmit_sc; int secy_init_macsec(struct ieee802_1x_kay *kay); int secy_deinit_macsec(struct ieee802_1x_kay *kay); @@ -25,6 +21,7 @@ int secy_deinit_macsec(struct ieee802_1x_kay *kay); int secy_cp_control_validate_frames(struct ieee802_1x_kay *kay, enum validate_frames vf); int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, Boolean flag); +int secy_cp_control_encrypt(struct ieee802_1x_kay *kay, Boolean enabled); int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean flag, u32 win); int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, u64 cs); int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay, @@ -32,27 +29,29 @@ int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay, int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, Boolean flag); /****** KaY -> SecY *******/ +int secy_get_capability(struct ieee802_1x_kay *kay, enum macsec_cap *cap); int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay, struct receive_sa *rxsa); int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay, struct transmit_sa *txsa); int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay, struct transmit_sa *txsa); -int secy_get_available_receive_sc(struct ieee802_1x_kay *kay, u32 *channel); int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc); int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc); int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa); +int secy_delete_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa); int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa); int secy_disable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa); -int secy_get_available_transmit_sc(struct ieee802_1x_kay *kay, u32 *channel); int secy_create_transmit_sc(struct ieee802_1x_kay *kay, struct transmit_sc *txsc); int secy_delete_transmit_sc(struct ieee802_1x_kay *kay, struct transmit_sc *txsc); int secy_create_transmit_sa(struct ieee802_1x_kay *kay, struct transmit_sa *txsa); +int secy_delete_transmit_sa(struct ieee802_1x_kay *kay, + struct transmit_sa *txsa); int secy_enable_transmit_sa(struct ieee802_1x_kay *kay, struct transmit_sa *txsa); int secy_disable_transmit_sa(struct ieee802_1x_kay *kay, diff --git a/src/radius/radius.c b/src/radius/radius.c index 407e4f8b9614..07240ea2243d 100644 --- a/src/radius/radius.c +++ b/src/radius/radius.c @@ -210,7 +210,7 @@ static const struct radius_attr_type radius_attrs[] = { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", + { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords", RADIUS_ATTR_INT32 }, @@ -250,6 +250,8 @@ static const struct radius_attr_type radius_attrs[] = { RADIUS_ATTR_MOBILITY_DOMAIN_ID, "Mobility-Domain-Id", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_WLAN_HESSID, "WLAN-HESSID", RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_WLAN_REASON_CODE, "WLAN-Reason-Code", + RADIUS_ATTR_INT32 }, { RADIUS_ATTR_WLAN_PAIRWISE_CIPHER, "WLAN-Pairwise-Cipher", RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_WLAN_GROUP_CIPHER, "WLAN-Group-Cipher", @@ -631,6 +633,9 @@ struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, size_t buf_needed; struct radius_attr_hdr *attr; + if (TEST_FAIL()) + return NULL; + if (data_len > RADIUS_MAX_ATTR_LEN) { wpa_printf(MSG_ERROR, "radius_msg_add_attr: too long attribute (%lu bytes)", (unsigned long) data_len); @@ -960,10 +965,9 @@ static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, } len = vhdr->vendor_length - sizeof(*vhdr); - data = os_malloc(len); + data = os_memdup(pos + sizeof(*vhdr), len); if (data == NULL) return NULL; - os_memcpy(data, pos + sizeof(*vhdr), len); if (alen) *alen = len; return data; @@ -1040,12 +1044,11 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len, return NULL; } - res = os_malloc(plain[0]); + res = os_memdup(plain + 1, plain[0]); if (res == NULL) { os_free(plain); return NULL; } - os_memcpy(res, plain + 1, plain[0]); if (reslen) *reslen = plain[0]; os_free(plain); @@ -1594,10 +1597,9 @@ char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, goto out; /* alloc writable memory for decryption */ - buf = os_malloc(fdlen); + buf = os_memdup(fdata, fdlen); if (buf == NULL) goto out; - os_memcpy(buf, fdata, fdlen); buflen = fdlen; /* init pointers */ @@ -1684,12 +1686,11 @@ int radius_copy_class(struct radius_class_data *dst, dst->count = 0; for (i = 0; i < src->count; i++) { - dst->attr[i].data = os_malloc(src->attr[i].len); + dst->attr[i].data = os_memdup(src->attr[i].data, + src->attr[i].len); if (dst->attr[i].data == NULL) break; dst->count++; - os_memcpy(dst->attr[i].data, src->attr[i].data, - src->attr[i].len); dst->attr[i].len = src->attr[i].len; } diff --git a/src/radius/radius.h b/src/radius/radius.h index cd510d2c88e2..630c0f9d0bc5 100644 --- a/src/radius/radius.h +++ b/src/radius/radius.h @@ -102,8 +102,13 @@ enum { RADIUS_ATTR_USER_NAME = 1, RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES = 130, RADIUS_ATTR_LOCATION_CAPABLE = 131, RADIUS_ATTR_REQUESTED_LOCATION_INFO = 132, + RADIUS_ATTR_GSS_ACCEPTOR_SERVICE_NAME = 164, + RADIUS_ATTR_GSS_ACCEPTOR_HOST_NAME = 165, + RADIUS_ATTR_GSS_ACCEPTOR_SERVICE_SPECIFICS = 166, + RADIUS_ATTR_GSS_ACCEPTOR_REALM_NAME = 167, RADIUS_ATTR_MOBILITY_DOMAIN_ID = 177, RADIUS_ATTR_WLAN_HESSID = 181, + RADIUS_ATTR_WLAN_REASON_CODE = 185, RADIUS_ATTR_WLAN_PAIRWISE_CIPHER = 186, RADIUS_ATTR_WLAN_GROUP_CIPHER = 187, RADIUS_ATTR_WLAN_AKM_SUITE = 188, @@ -193,6 +198,11 @@ enum { RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION = 3, RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ = 4, RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL = 5, + RADIUS_VENDOR_ATTR_WFA_HS20_ROAMING_CONSORTIUM = 6, + RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILENAME = 7, + RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP = 8, + RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING = 9, + RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL = 10, }; #ifdef _MSC_VER diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c index 06c804d132fd..a87ee745eb28 100644 --- a/src/radius/radius_client.c +++ b/src/radius/radius_client.c @@ -904,13 +904,13 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) switch (res) { case RADIUS_RX_PROCESSED: radius_msg_free(msg); - /* continue */ + /* fall through */ case RADIUS_RX_QUEUED: radius_client_msg_free(req); return; case RADIUS_RX_INVALID_AUTHENTICATOR: invalid_authenticator++; - /* continue */ + /* fall through */ case RADIUS_RX_UNKNOWN: /* continue with next handler */ break; diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c index 8a3d7e0324bc..aaa3fc26723a 100644 --- a/src/radius/radius_das.c +++ b/src/radius/radius_das.c @@ -27,6 +27,7 @@ struct radius_das_data { void *ctx; enum radius_das_res (*disconnect)(void *ctx, struct radius_das_attrs *attr); + enum radius_das_res (*coa)(void *ctx, struct radius_das_attrs *attr); }; @@ -161,6 +162,10 @@ static struct radius_msg * radius_das_disconnect(struct radius_das_data *das, abuf, from_port); error = 508; break; + case RADIUS_DAS_COA_FAILED: + /* not used with Disconnect-Request */ + error = 405; + break; case RADIUS_DAS_SUCCESS: error = 0; break; @@ -184,6 +189,195 @@ fail: } +static struct radius_msg * radius_das_coa(struct radius_das_data *das, + struct radius_msg *msg, + const char *abuf, int from_port) +{ + struct radius_hdr *hdr; + struct radius_msg *reply; + u8 allowed[] = { + RADIUS_ATTR_USER_NAME, + RADIUS_ATTR_NAS_IP_ADDRESS, + RADIUS_ATTR_CALLING_STATION_ID, + RADIUS_ATTR_NAS_IDENTIFIER, + RADIUS_ATTR_ACCT_SESSION_ID, + RADIUS_ATTR_ACCT_MULTI_SESSION_ID, + RADIUS_ATTR_EVENT_TIMESTAMP, + RADIUS_ATTR_MESSAGE_AUTHENTICATOR, + RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, +#ifdef CONFIG_HS20 + RADIUS_ATTR_VENDOR_SPECIFIC, +#endif /* CONFIG_HS20 */ +#ifdef CONFIG_IPV6 + RADIUS_ATTR_NAS_IPV6_ADDRESS, +#endif /* CONFIG_IPV6 */ + 0 + }; + int error = 405; + u8 attr; + enum radius_das_res res; + struct radius_das_attrs attrs; + u8 *buf; + size_t len; + char tmp[100]; + u8 sta_addr[ETH_ALEN]; + + hdr = radius_msg_get_hdr(msg); + + if (!das->coa) { + wpa_printf(MSG_INFO, "DAS: CoA not supported"); + goto fail; + } + + attr = radius_msg_find_unlisted_attr(msg, allowed); + if (attr) { + wpa_printf(MSG_INFO, + "DAS: Unsupported attribute %u in CoA-Request from %s:%d", + attr, abuf, from_port); + error = 401; + goto fail; + } + + os_memset(&attrs, 0, sizeof(attrs)); + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, + &buf, &len, NULL) == 0) { + if (len != 4) { + wpa_printf(MSG_INFO, "DAS: Invalid NAS-IP-Address from %s:%d", + abuf, from_port); + error = 407; + goto fail; + } + attrs.nas_ip_addr = buf; + } + +#ifdef CONFIG_IPV6 + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, + &buf, &len, NULL) == 0) { + if (len != 16) { + wpa_printf(MSG_INFO, "DAS: Invalid NAS-IPv6-Address from %s:%d", + abuf, from_port); + error = 407; + goto fail; + } + attrs.nas_ipv6_addr = buf; + } +#endif /* CONFIG_IPV6 */ + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER, + &buf, &len, NULL) == 0) { + attrs.nas_identifier = buf; + attrs.nas_identifier_len = len; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID, + &buf, &len, NULL) == 0) { + if (len >= sizeof(tmp)) + len = sizeof(tmp) - 1; + os_memcpy(tmp, buf, len); + tmp[len] = '\0'; + if (hwaddr_aton2(tmp, sta_addr) < 0) { + wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id " + "'%s' from %s:%d", tmp, abuf, from_port); + error = 407; + goto fail; + } + attrs.sta_addr = sta_addr; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, + &buf, &len, NULL) == 0) { + attrs.user_name = buf; + attrs.user_name_len = len; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID, + &buf, &len, NULL) == 0) { + attrs.acct_session_id = buf; + attrs.acct_session_id_len = len; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID, + &buf, &len, NULL) == 0) { + attrs.acct_multi_session_id = buf; + attrs.acct_multi_session_id_len = len; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + &buf, &len, NULL) == 0) { + attrs.cui = buf; + attrs.cui_len = len; + } + +#ifdef CONFIG_HS20 + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, + &buf, &len, NULL) == 0) { + if (len < 10 || WPA_GET_BE32(buf) != RADIUS_VENDOR_ID_WFA || + buf[4] != RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING || + buf[5] < 6) { + wpa_printf(MSG_INFO, + "DAS: Unsupported attribute %u in CoA-Request from %s:%d", + attr, abuf, from_port); + error = 401; + goto fail; + } + attrs.hs20_t_c_filtering = &buf[6]; + } + + if (!attrs.hs20_t_c_filtering) { + wpa_printf(MSG_INFO, + "DAS: No supported authorization change attribute in CoA-Request from %s:%d", + abuf, from_port); + error = 402; + goto fail; + } +#endif /* CONFIG_HS20 */ + + res = das->coa(das->ctx, &attrs); + switch (res) { + case RADIUS_DAS_NAS_MISMATCH: + wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d", + abuf, from_port); + error = 403; + break; + case RADIUS_DAS_SESSION_NOT_FOUND: + wpa_printf(MSG_INFO, + "DAS: Session not found for request from %s:%d", + abuf, from_port); + error = 503; + break; + case RADIUS_DAS_MULTI_SESSION_MATCH: + wpa_printf(MSG_INFO, + "DAS: Multiple sessions match for request from %s:%d", + abuf, from_port); + error = 508; + break; + case RADIUS_DAS_COA_FAILED: + wpa_printf(MSG_INFO, "DAS: CoA failed for request from %s:%d", + abuf, from_port); + error = 407; + break; + case RADIUS_DAS_SUCCESS: + error = 0; + break; + } + +fail: + reply = radius_msg_new(error ? RADIUS_CODE_COA_NAK : + RADIUS_CODE_COA_ACK, hdr->identifier); + if (!reply) + return NULL; + + if (error && + !radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, error)) { + radius_msg_free(reply); + return NULL; + } + + return reply; +} + + static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct radius_das_data *das = eloop_ctx; @@ -219,7 +413,8 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d", len, abuf, from_port); - if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) { + if (das->client_addr.u.v4.s_addr && + das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) { wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client"); return; } @@ -270,19 +465,7 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) reply = radius_das_disconnect(das, msg, abuf, from_port); break; case RADIUS_CODE_COA_REQUEST: - /* TODO */ - reply = radius_msg_new(RADIUS_CODE_COA_NAK, - hdr->identifier); - if (reply == NULL) - break; - - /* Unsupported Service */ - if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, - 405)) { - radius_msg_free(reply); - reply = NULL; - break; - } + reply = radius_das_coa(das, msg, abuf, from_port); break; default: wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in " @@ -369,17 +552,17 @@ radius_das_init(struct radius_das_conf *conf) conf->require_message_authenticator; das->ctx = conf->ctx; das->disconnect = conf->disconnect; + das->coa = conf->coa; os_memcpy(&das->client_addr, conf->client_addr, sizeof(das->client_addr)); - das->shared_secret = os_malloc(conf->shared_secret_len); + das->shared_secret = os_memdup(conf->shared_secret, + conf->shared_secret_len); if (das->shared_secret == NULL) { radius_das_deinit(das); return NULL; } - os_memcpy(das->shared_secret, conf->shared_secret, - conf->shared_secret_len); das->shared_secret_len = conf->shared_secret_len; das->sock = radius_das_open_socket(conf->port); diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h index 9863fdc1eaca..233d662f631b 100644 --- a/src/radius/radius_das.h +++ b/src/radius/radius_das.h @@ -16,6 +16,7 @@ enum radius_das_res { RADIUS_DAS_NAS_MISMATCH, RADIUS_DAS_SESSION_NOT_FOUND, RADIUS_DAS_MULTI_SESSION_MATCH, + RADIUS_DAS_COA_FAILED, }; struct radius_das_attrs { @@ -35,6 +36,9 @@ struct radius_das_attrs { size_t acct_multi_session_id_len; const u8 *cui; size_t cui_len; + + /* Authorization changes */ + const u8 *hs20_t_c_filtering; }; struct radius_das_conf { @@ -48,6 +52,7 @@ struct radius_das_conf { void *ctx; enum radius_das_res (*disconnect)(void *ctx, struct radius_das_attrs *attr); + enum radius_das_res (*coa)(void *ctx, struct radius_das_attrs *attr); }; struct radius_das_data * diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index 744283c7dc9d..e3afc0d53298 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -26,9 +26,14 @@ #define RADIUS_SESSION_TIMEOUT 60 /** + * RADIUS_SESSION_MAINTAIN - Completed session expiration timeout in seconds + */ +#define RADIUS_SESSION_MAINTAIN 5 + +/** * RADIUS_MAX_SESSION - Maximum number of active sessions */ -#define RADIUS_MAX_SESSION 100 +#define RADIUS_MAX_SESSION 1000 /** * RADIUS_MAX_MSG_LEN - Maximum message length for incoming RADIUS messages @@ -75,6 +80,7 @@ struct radius_session { struct eap_eapol_interface *eap_if; char *username; /* from User-Name attribute */ char *nas_ip; + u8 mac_addr[ETH_ALEN]; /* from Calling-Station-Id attribute */ struct radius_msg *last_msg; char *last_from_addr; @@ -87,8 +93,11 @@ struct radius_session { unsigned int remediation:1; unsigned int macacl:1; + unsigned int t_c_filtering:1; struct hostapd_radius_attr *accept_attr; + + u32 t_c_timestamp; /* Last read T&C timestamp from user DB */ }; /** @@ -106,6 +115,14 @@ struct radius_client { int shared_secret_len; struct radius_session *sessions; struct radius_server_counters counters; + + u8 next_dac_identifier; + struct radius_msg *pending_dac_coa_req; + u8 pending_dac_coa_id; + u8 pending_dac_coa_addr[ETH_ALEN]; + struct radius_msg *pending_dac_disconnect_req; + u8 pending_dac_disconnect_id; + u8 pending_dac_disconnect_addr[ETH_ALEN]; }; /** @@ -267,6 +284,8 @@ struct radius_server_data { unsigned int tls_session_lifetime; + unsigned int tls_flags; + /** * wps - Wi-Fi Protected Setup context * @@ -339,6 +358,8 @@ struct radius_server_data { char *subscr_remediation_url; u8 subscr_remediation_method; + char *t_c_server_url; + #ifdef CONFIG_SQLITE sqlite3 *db; #endif /* CONFIG_SQLITE */ @@ -621,8 +642,8 @@ radius_server_get_new_session(struct radius_server_data *data, struct radius_client *client, struct radius_msg *msg, const char *from_addr) { - u8 *user; - size_t user_len; + u8 *user, *id; + size_t user_len, id_len; int res; struct radius_session *sess; struct eap_config eap_conf; @@ -657,17 +678,32 @@ radius_server_get_new_session(struct radius_server_data *data, sess->username = os_malloc(user_len * 4 + 1); if (sess->username == NULL) { - radius_server_session_free(data, sess); + radius_server_session_remove(data, sess); return NULL; } printf_encode(sess->username, user_len * 4 + 1, user, user_len); sess->nas_ip = os_strdup(from_addr); if (sess->nas_ip == NULL) { - radius_server_session_free(data, sess); + radius_server_session_remove(data, sess); return NULL; } + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID, &id, + &id_len, NULL) == 0) { + char buf[3 * ETH_ALEN]; + + os_memset(buf, 0, sizeof(buf)); + if (id_len >= sizeof(buf)) + id_len = sizeof(buf) - 1; + os_memcpy(buf, id, id_len); + if (hwaddr_aton2(buf, sess->mac_addr) < 0) + os_memset(sess->mac_addr, 0, ETH_ALEN); + else + RADIUS_DEBUG("Calling-Station-Id: " MACSTR, + MAC2STR(sess->mac_addr)); + } + srv_log(sess, "New session created"); os_memset(&eap_conf, 0, sizeof(eap_conf)); @@ -691,13 +727,14 @@ radius_server_get_new_session(struct radius_server_data *data, eap_conf.server_id_len = os_strlen(data->server_id); eap_conf.erp = data->erp; eap_conf.tls_session_lifetime = data->tls_session_lifetime; + eap_conf.tls_flags = data->tls_flags; radius_server_testing_options(sess, &eap_conf); sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb, &eap_conf); if (sess->eap == NULL) { RADIUS_DEBUG("Failed to initialize EAP state machine for the " "new session"); - radius_server_session_free(data, sess); + radius_server_session_remove(data, sess); return NULL; } sess->eap_if = eap_get_interface(sess->eap); @@ -710,6 +747,125 @@ radius_server_get_new_session(struct radius_server_data *data, } +#ifdef CONFIG_HS20 +static void radius_srv_hs20_t_c_pending(struct radius_session *sess) +{ +#ifdef CONFIG_SQLITE + char *sql; + char addr[3 * ETH_ALEN], *id_str; + const u8 *id; + size_t id_len; + + if (!sess->server->db || !sess->eap || + is_zero_ether_addr(sess->mac_addr)) + return; + + os_snprintf(addr, sizeof(addr), MACSTR, MAC2STR(sess->mac_addr)); + + id = eap_get_identity(sess->eap, &id_len); + if (!id) + return; + id_str = os_malloc(id_len + 1); + if (!id_str) + return; + os_memcpy(id_str, id, id_len); + id_str[id_len] = '\0'; + + sql = sqlite3_mprintf("INSERT OR REPLACE INTO pending_tc (mac_addr,identity) VALUES (%Q,%Q)", + addr, id_str); + os_free(id_str); + if (!sql) + return; + + if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) != + SQLITE_OK) { + RADIUS_ERROR("Failed to add pending_tc entry into sqlite database: %s", + sqlite3_errmsg(sess->server->db)); + } + sqlite3_free(sql); +#endif /* CONFIG_SQLITE */ +} +#endif /* CONFIG_HS20 */ + + +static void radius_server_add_session(struct radius_session *sess) +{ +#ifdef CONFIG_SQLITE + char *sql; + char addr_txt[ETH_ALEN * 3]; + struct os_time now; + + if (!sess->server->db) + return; + + + os_snprintf(addr_txt, sizeof(addr_txt), MACSTR, + MAC2STR(sess->mac_addr)); + + os_get_time(&now); + sql = sqlite3_mprintf("INSERT OR REPLACE INTO current_sessions(mac_addr,identity,start_time,nas,hs20_t_c_filtering) VALUES (%Q,%Q,%d,%Q,%u)", + addr_txt, sess->username, now.sec, + sess->nas_ip, sess->t_c_filtering); + if (sql) { + if (sqlite3_exec(sess->server->db, sql, NULL, NULL, + NULL) != SQLITE_OK) { + RADIUS_ERROR("Failed to add current_sessions entry into sqlite database: %s", + sqlite3_errmsg(sess->server->db)); + } + sqlite3_free(sql); + } +#endif /* CONFIG_SQLITE */ +} + + +static void db_update_last_msk(struct radius_session *sess, const char *msk) +{ +#ifdef CONFIG_RADIUS_TEST +#ifdef CONFIG_SQLITE + char *sql = NULL; + char *id_str = NULL; + const u8 *id; + size_t id_len; + const char *serial_num; + + if (!sess->server->db) + return; + + serial_num = eap_get_serial_num(sess->eap); + if (serial_num) { + id_len = 5 + os_strlen(serial_num) + 1; + id_str = os_malloc(id_len); + if (!id_str) + return; + os_snprintf(id_str, id_len, "cert-%s", serial_num); + } else { + id = eap_get_identity(sess->eap, &id_len); + if (!id) + return; + id_str = os_malloc(id_len + 1); + if (!id_str) + return; + os_memcpy(id_str, id, id_len); + id_str[id_len] = '\0'; + } + + sql = sqlite3_mprintf("UPDATE users SET last_msk=%Q WHERE identity=%Q", + msk, id_str); + os_free(id_str); + if (!sql) + return; + + if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) != + SQLITE_OK) { + RADIUS_DEBUG("Failed to update last_msk: %s", + sqlite3_errmsg(sess->server->db)); + } + sqlite3_free(sql); +#endif /* CONFIG_SQLITE */ +#endif /* CONFIG_RADIUS_TEST */ +} + + static struct radius_msg * radius_server_encapsulate_eap(struct radius_server_data *data, struct radius_client *client, @@ -720,6 +876,7 @@ radius_server_encapsulate_eap(struct radius_server_data *data, int code; unsigned int sess_id; struct radius_hdr *hdr = radius_msg_get_hdr(request); + u16 reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED; if (sess->eap_if->eapFail) { sess->eap_if->eapFail = FALSE; @@ -754,9 +911,18 @@ radius_server_encapsulate_eap(struct radius_server_data *data, if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) { int len; #ifdef CONFIG_RADIUS_TEST + char buf[2 * 64 + 1]; + + len = sess->eap_if->eapKeyDataLen; + if (len > 64) + len = 64; + len = wpa_snprintf_hex(buf, sizeof(buf), + sess->eap_if->eapKeyData, len); + buf[len] = '\0'; + if (data->dump_msk_file) { FILE *f; - char buf[2 * 64 + 1]; + f = fopen(data->dump_msk_file, "a"); if (f) { len = sess->eap_if->eapKeyDataLen; @@ -770,6 +936,8 @@ radius_server_encapsulate_eap(struct radius_server_data *data, fclose(f); } } + + db_update_last_msk(sess, buf); #endif /* CONFIG_RADIUS_TEST */ if (sess->eap_if->eapKeyDataLen > 64) { len = 32; @@ -812,6 +980,61 @@ radius_server_encapsulate_eap(struct radius_server_data *data, RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem"); } } + + if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->t_c_filtering) { + u8 buf[4] = { 0x01, 0x00, 0x00, 0x00 }; /* E=1 */ + const char *url = data->t_c_server_url, *pos; + char *url2, *end2, *pos2; + size_t url_len; + + if (!radius_msg_add_wfa( + msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING, + buf, sizeof(buf))) { + RADIUS_DEBUG("Failed to add WFA-HS20-T-C-Filtering"); + radius_msg_free(msg); + return NULL; + } + + if (!url) { + RADIUS_DEBUG("No t_c_server_url configured"); + radius_msg_free(msg); + return NULL; + } + + pos = os_strstr(url, "@1@"); + if (!pos) { + RADIUS_DEBUG("No @1@ macro in t_c_server_url"); + radius_msg_free(msg); + return NULL; + } + + url_len = os_strlen(url) + ETH_ALEN * 3 - 1 - 3; + url2 = os_malloc(url_len + 1); + if (!url2) { + RADIUS_DEBUG("Failed to allocate room for T&C Server URL"); + os_free(url2); + radius_msg_free(msg); + return NULL; + } + pos2 = url2; + end2 = url2 + url_len + 1; + os_memcpy(pos2, url, pos - url); + pos2 += pos - url; + os_snprintf(pos2, end2 - pos2, MACSTR, MAC2STR(sess->mac_addr)); + pos2 += ETH_ALEN * 3 - 1; + os_memcpy(pos2, pos + 3, os_strlen(pos + 3)); + if (!radius_msg_add_wfa(msg, + RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL, + (const u8 *) url2, url_len)) { + RADIUS_DEBUG("Failed to add WFA-HS20-T-C-URL"); + os_free(url2); + radius_msg_free(msg); + return NULL; + } + os_free(url2); + + radius_srv_hs20_t_c_pending(sess); + } #endif /* CONFIG_HS20 */ if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { @@ -833,12 +1056,24 @@ radius_server_encapsulate_eap(struct radius_server_data *data, } } + if (code == RADIUS_CODE_ACCESS_REJECT) { + if (radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE, + reason) < 0) { + RADIUS_DEBUG("Failed to add WLAN-Reason-Code attribute"); + radius_msg_free(msg); + return NULL; + } + } + if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, client->shared_secret_len, hdr->authenticator) < 0) { RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); } + if (code == RADIUS_CODE_ACCESS_ACCEPT) + radius_server_add_session(sess); + return msg; } @@ -985,6 +1220,51 @@ static int radius_server_reject(struct radius_server_data *data, } +static void radius_server_hs20_t_c_check(struct radius_session *sess, + struct radius_msg *msg) +{ +#ifdef CONFIG_HS20 + u8 *buf, *pos, *end, type, sublen, *timestamp = NULL; + size_t len; + + buf = NULL; + for (;;) { + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, + &buf, &len, buf) < 0) + break; + if (len < 6) + continue; + pos = buf; + end = buf + len; + if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA) + continue; + pos += 4; + + type = *pos++; + sublen = *pos++; + if (sublen < 2) + continue; /* invalid length */ + sublen -= 2; /* skip header */ + if (pos + sublen > end) + continue; /* invalid WFA VSA */ + + if (type == RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP && len >= 4) { + timestamp = pos; + break; + } + } + + if (!timestamp) + return; + RADIUS_DEBUG("HS20-Timestamp: %u", WPA_GET_BE32(timestamp)); + if (sess->t_c_timestamp != WPA_GET_BE32(timestamp)) { + RADIUS_DEBUG("Last read T&C timestamp does not match HS20-Timestamp --> require filtering"); + sess->t_c_filtering = 1; + } +#endif /* CONFIG_HS20 */ +} + + static int radius_server_request(struct radius_server_data *data, struct radius_msg *msg, struct sockaddr *from, socklen_t fromlen, @@ -1057,7 +1337,7 @@ static int radius_server_request(struct radius_server_data *data, "message"); return -1; } - + eap = radius_msg_get_eap(msg); if (eap == NULL && sess->macacl) { reply = radius_server_macacl(data, client, sess, msg); @@ -1115,10 +1395,15 @@ static int radius_server_request(struct radius_server_data *data, if (sess->eap_if->eapSuccess || sess->eap_if->eapFail) is_complete = 1; - if (sess->eap_if->eapFail) + if (sess->eap_if->eapFail) { srv_log(sess, "EAP authentication failed"); - else if (sess->eap_if->eapSuccess) + db_update_last_msk(sess, "FAIL"); + } else if (sess->eap_if->eapSuccess) { srv_log(sess, "EAP authentication succeeded"); + } + + if (sess->eap_if->eapSuccess) + radius_server_hs20_t_c_check(sess, msg); reply = radius_server_encapsulate_eap(data, client, sess, msg); @@ -1172,7 +1457,7 @@ send_reply: sess->sess_id); eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess); - eloop_register_timeout(10, 0, + eloop_register_timeout(RADIUS_SESSION_MAINTAIN, 0, radius_server_session_remove_timeout, data, sess); } @@ -1181,6 +1466,116 @@ send_reply: } +static void +radius_server_receive_disconnect_resp(struct radius_server_data *data, + struct radius_client *client, + struct radius_msg *msg, int ack) +{ + struct radius_hdr *hdr; + + if (!client->pending_dac_disconnect_req) { + RADIUS_DEBUG("Ignore unexpected Disconnect response"); + radius_msg_free(msg); + return; + } + + hdr = radius_msg_get_hdr(msg); + if (hdr->identifier != client->pending_dac_disconnect_id) { + RADIUS_DEBUG("Ignore unexpected Disconnect response with unexpected identifier %u (expected %u)", + hdr->identifier, + client->pending_dac_disconnect_id); + radius_msg_free(msg); + return; + } + + if (radius_msg_verify(msg, (const u8 *) client->shared_secret, + client->shared_secret_len, + client->pending_dac_disconnect_req, 0)) { + RADIUS_DEBUG("Ignore Disconnect response with invalid authenticator"); + radius_msg_free(msg); + return; + } + + RADIUS_DEBUG("Disconnect-%s received for " MACSTR, + ack ? "ACK" : "NAK", + MAC2STR(client->pending_dac_disconnect_addr)); + + radius_msg_free(msg); + radius_msg_free(client->pending_dac_disconnect_req); + client->pending_dac_disconnect_req = NULL; +} + + +static void radius_server_receive_coa_resp(struct radius_server_data *data, + struct radius_client *client, + struct radius_msg *msg, int ack) +{ + struct radius_hdr *hdr; +#ifdef CONFIG_SQLITE + char addrtxt[3 * ETH_ALEN]; + char *sql; + int res; +#endif /* CONFIG_SQLITE */ + + if (!client->pending_dac_coa_req) { + RADIUS_DEBUG("Ignore unexpected CoA response"); + radius_msg_free(msg); + return; + } + + hdr = radius_msg_get_hdr(msg); + if (hdr->identifier != client->pending_dac_coa_id) { + RADIUS_DEBUG("Ignore unexpected CoA response with unexpected identifier %u (expected %u)", + hdr->identifier, + client->pending_dac_coa_id); + radius_msg_free(msg); + return; + } + + if (radius_msg_verify(msg, (const u8 *) client->shared_secret, + client->shared_secret_len, + client->pending_dac_coa_req, 0)) { + RADIUS_DEBUG("Ignore CoA response with invalid authenticator"); + radius_msg_free(msg); + return; + } + + RADIUS_DEBUG("CoA-%s received for " MACSTR, + ack ? "ACK" : "NAK", + MAC2STR(client->pending_dac_coa_addr)); + + radius_msg_free(msg); + radius_msg_free(client->pending_dac_coa_req); + client->pending_dac_coa_req = NULL; + +#ifdef CONFIG_SQLITE + if (!data->db) + return; + + os_snprintf(addrtxt, sizeof(addrtxt), MACSTR, + MAC2STR(client->pending_dac_coa_addr)); + + if (ack) { + sql = sqlite3_mprintf("UPDATE current_sessions SET hs20_t_c_filtering=0, waiting_coa_ack=0, coa_ack_received=1 WHERE mac_addr=%Q", + addrtxt); + } else { + sql = sqlite3_mprintf("UPDATE current_sessions SET waiting_coa_ack=0 WHERE mac_addr=%Q", + addrtxt); + } + if (!sql) + return; + + res = sqlite3_exec(data->db, sql, NULL, NULL, NULL); + sqlite3_free(sql); + if (res != SQLITE_OK) { + RADIUS_ERROR("Failed to update current_sessions entry: %s", + sqlite3_errmsg(data->db)); + return; + } +#endif /* CONFIG_SQLITE */ +} + + static void radius_server_receive_auth(int sock, void *eloop_ctx, void *sock_ctx) { @@ -1261,6 +1656,26 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, radius_msg_dump(msg); } + if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_DISCONNECT_ACK) { + radius_server_receive_disconnect_resp(data, client, msg, 1); + return; + } + + if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_DISCONNECT_NAK) { + radius_server_receive_disconnect_resp(data, client, msg, 0); + return; + } + + if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_COA_ACK) { + radius_server_receive_coa_resp(data, client, msg, 1); + return; + } + + if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_COA_NAK) { + radius_server_receive_coa_resp(data, client, msg, 0); + return; + } + if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCESS_REQUEST) { RADIUS_DEBUG("Unexpected RADIUS code %d", radius_msg_get_hdr(msg)->code); @@ -1521,6 +1936,8 @@ static void radius_server_free_clients(struct radius_server_data *data, radius_server_free_sessions(data, prev->sessions); os_free(prev->shared_secret); + radius_msg_free(prev->pending_dac_coa_req); + radius_msg_free(prev->pending_dac_disconnect_req); os_free(prev); } } @@ -1749,6 +2166,7 @@ radius_server_init(struct radius_server_conf *conf) data->erp = conf->erp; data->erp_domain = conf->erp_domain; data->tls_session_lifetime = conf->tls_session_lifetime; + data->tls_flags = conf->tls_flags; if (conf->subscr_remediation_url) { data->subscr_remediation_url = @@ -1756,6 +2174,9 @@ radius_server_init(struct radius_server_conf *conf) } data->subscr_remediation_method = conf->subscr_remediation_method; + if (conf->t_c_server_url) + data->t_c_server_url = os_strdup(conf->t_c_server_url); + #ifdef CONFIG_SQLITE if (conf->sqlite_file) { if (sqlite3_open(conf->sqlite_file, &data->db)) { @@ -1872,6 +2293,7 @@ void radius_server_deinit(struct radius_server_data *data) os_free(data->dump_msk_file); #endif /* CONFIG_RADIUS_TEST */ os_free(data->subscr_remediation_url); + os_free(data->t_c_server_url); #ifdef CONFIG_SQLITE if (data->db) @@ -2040,6 +2462,7 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity, sess->accept_attr = user->accept_attr; sess->remediation = user->remediation; sess->macacl = user->macacl; + sess->t_c_timestamp = user->t_c_timestamp; } if (ret) { @@ -2166,3 +2589,212 @@ void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx) radius_msg_free(msg); } + + +#ifdef CONFIG_SQLITE + +struct db_session_fields { + char *identity; + char *nas; + int hs20_t_c_filtering; + int waiting_coa_ack; + int coa_ack_received; +}; + + +static int get_db_session_fields(void *ctx, int argc, char *argv[], char *col[]) +{ + struct db_session_fields *fields = ctx; + int i; + + for (i = 0; i < argc; i++) { + if (!argv[i]) + continue; + + RADIUS_DEBUG("Session DB: %s=%s", col[i], argv[i]); + + if (os_strcmp(col[i], "identity") == 0) { + os_free(fields->identity); + fields->identity = os_strdup(argv[i]); + } else if (os_strcmp(col[i], "nas") == 0) { + os_free(fields->nas); + fields->nas = os_strdup(argv[i]); + } else if (os_strcmp(col[i], "hs20_t_c_filtering") == 0) { + fields->hs20_t_c_filtering = atoi(argv[i]); + } else if (os_strcmp(col[i], "waiting_coa_ack") == 0) { + fields->waiting_coa_ack = atoi(argv[i]); + } else if (os_strcmp(col[i], "coa_ack_received") == 0) { + fields->coa_ack_received = atoi(argv[i]); + } + } + + return 0; +} + + +static void free_db_session_fields(struct db_session_fields *fields) +{ + os_free(fields->identity); + fields->identity = NULL; + os_free(fields->nas); + fields->nas = NULL; +} + +#endif /* CONFIG_SQLITE */ + + +int radius_server_dac_request(struct radius_server_data *data, const char *req) +{ +#ifdef CONFIG_SQLITE + char *sql; + int res; + int disconnect; + const char *pos = req; + u8 addr[ETH_ALEN]; + char addrtxt[3 * ETH_ALEN]; + int t_c_clear = 0; + struct db_session_fields fields; + struct sockaddr_in das; + struct radius_client *client; + struct radius_msg *msg; + struct wpabuf *buf; + u8 identifier; + struct os_time now; + + if (!data) + return -1; + + /* req: <disconnect|coa> <MAC Address> [t_c_clear] */ + + if (os_strncmp(pos, "disconnect ", 11) == 0) { + disconnect = 1; + pos += 11; + } else if (os_strncmp(req, "coa ", 4) == 0) { + disconnect = 0; + pos += 4; + } else { + return -1; + } + + if (hwaddr_aton(pos, addr)) + return -1; + pos = os_strchr(pos, ' '); + if (pos) { + if (os_strstr(pos, "t_c_clear")) + t_c_clear = 1; + } + + if (!disconnect && !t_c_clear) { + RADIUS_ERROR("DAC request for CoA without any authorization change"); + return -1; + } + + if (!data->db) { + RADIUS_ERROR("SQLite database not in use"); + return -1; + } + + os_snprintf(addrtxt, sizeof(addrtxt), MACSTR, MAC2STR(addr)); + + sql = sqlite3_mprintf("SELECT * FROM current_sessions WHERE mac_addr=%Q", + addrtxt); + if (!sql) + return -1; + + os_memset(&fields, 0, sizeof(fields)); + res = sqlite3_exec(data->db, sql, get_db_session_fields, &fields, NULL); + sqlite3_free(sql); + if (res != SQLITE_OK) { + RADIUS_ERROR("Failed to find matching current_sessions entry from sqlite database: %s", + sqlite3_errmsg(data->db)); + free_db_session_fields(&fields); + return -1; + } + + if (!fields.nas) { + RADIUS_ERROR("No NAS information found from current_sessions"); + free_db_session_fields(&fields); + return -1; + } + + os_memset(&das, 0, sizeof(das)); + das.sin_family = AF_INET; + das.sin_addr.s_addr = inet_addr(fields.nas); + das.sin_port = htons(3799); + + free_db_session_fields(&fields); + + client = radius_server_get_client(data, &das.sin_addr, 0); + if (!client) { + RADIUS_ERROR("No NAS information available to protect the packet"); + return -1; + } + + identifier = client->next_dac_identifier++; + + msg = radius_msg_new(disconnect ? RADIUS_CODE_DISCONNECT_REQUEST : + RADIUS_CODE_COA_REQUEST, identifier); + if (!msg) + return -1; + + os_snprintf(addrtxt, sizeof(addrtxt), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(addr)); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, + (u8 *) addrtxt, os_strlen(addrtxt))) { + RADIUS_ERROR("Could not add Calling-Station-Id"); + radius_msg_free(msg); + return -1; + } + + if (!disconnect && t_c_clear) { + u8 val[4] = { 0x00, 0x00, 0x00, 0x00 }; /* E=0 */ + + if (!radius_msg_add_wfa( + msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING, + val, sizeof(val))) { + RADIUS_DEBUG("Failed to add WFA-HS20-T-C-Filtering"); + radius_msg_free(msg); + return -1; + } + } + + os_get_time(&now); + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, + now.sec)) { + RADIUS_ERROR("Failed to add Event-Timestamp attribute"); + radius_msg_free(msg); + return -1; + } + + radius_msg_finish_acct(msg, (u8 *) client->shared_secret, + client->shared_secret_len); + + if (wpa_debug_level <= MSG_MSGDUMP) + radius_msg_dump(msg); + + buf = radius_msg_get_buf(msg); + if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0, + (struct sockaddr *) &das, sizeof(das)) < 0) { + RADIUS_ERROR("Failed to send packet - sendto: %s", + strerror(errno)); + radius_msg_free(msg); + return -1; + } + + if (disconnect) { + radius_msg_free(client->pending_dac_disconnect_req); + client->pending_dac_disconnect_req = msg; + client->pending_dac_disconnect_id = identifier; + os_memcpy(client->pending_dac_disconnect_addr, addr, ETH_ALEN); + } else { + radius_msg_free(client->pending_dac_coa_req); + client->pending_dac_coa_req = msg; + client->pending_dac_coa_id = identifier; + os_memcpy(client->pending_dac_coa_addr, addr, ETH_ALEN); + } + + return 0; +#else /* CONFIG_SQLITE */ + return -1; +#endif /* CONFIG_SQLITE */ +} diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h index 7a25802c8152..167bbf5b2881 100644 --- a/src/radius/radius_server.h +++ b/src/radius/radius_server.h @@ -172,6 +172,8 @@ struct radius_server_conf { unsigned int tls_session_lifetime; + unsigned int tls_flags; + /** * wps - Wi-Fi Protected Setup context * @@ -231,6 +233,8 @@ struct radius_server_conf { char *subscr_remediation_url; u8 subscr_remediation_method; + + char *t_c_server_url; }; @@ -244,5 +248,6 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, size_t buflen); void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx); +int radius_server_dac_request(struct radius_server_data *data, const char *req); #endif /* RADIUS_SERVER_H */ diff --git a/src/rsn_supp/Makefile b/src/rsn_supp/Makefile index d5e61fe72dc8..c2d81f279195 100644 --- a/src/rsn_supp/Makefile +++ b/src/rsn_supp/Makefile @@ -10,7 +10,6 @@ include ../lib.rules CFLAGS += -DCONFIG_IEEE80211W CFLAGS += -DCONFIG_IEEE80211R -CFLAGS += -DCONFIG_PEERKEY CFLAGS += -DCONFIG_TDLS CFLAGS += -DCONFIG_WNM CFLAGS += -DIEEE8021X_EAPOL @@ -18,7 +17,6 @@ CFLAGS += -DIEEE8021X_EAPOL LIB_OBJS= \ pmksa_cache.o \ wpa_ft.o \ - peerkey.o \ tdls.o \ preauth.o \ wpa.o \ diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c deleted file mode 100644 index 79764d94b902..000000000000 --- a/src/rsn_supp/peerkey.c +++ /dev/null @@ -1,1155 +0,0 @@ -/* - * WPA Supplicant - PeerKey for Direct Link Setup (DLS) - * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" - -#ifdef CONFIG_PEERKEY - -#include "common.h" -#include "eloop.h" -#include "crypto/sha1.h" -#include "crypto/sha256.h" -#include "crypto/random.h" -#include "common/ieee802_11_defs.h" -#include "wpa.h" -#include "wpa_i.h" -#include "wpa_ie.h" -#include "peerkey.h" - - -static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len) -{ - os_memcpy(pos, ie, ie_len); - return pos + ie_len; -} - - -static u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len) -{ - *pos++ = WLAN_EID_VENDOR_SPECIFIC; - *pos++ = RSN_SELECTOR_LEN + data_len; - RSN_SELECTOR_PUT(pos, kde); - pos += RSN_SELECTOR_LEN; - os_memcpy(pos, data, data_len); - pos += data_len; - return pos; -} - - -static void wpa_supplicant_smk_timeout(void *eloop_ctx, void *timeout_ctx) -{ -#if 0 - struct wpa_sm *sm = eloop_ctx; - struct wpa_peerkey *peerkey = timeout_ctx; -#endif - /* TODO: time out SMK and any STK that was generated using this SMK */ -} - - -static void wpa_supplicant_peerkey_free(struct wpa_sm *sm, - struct wpa_peerkey *peerkey) -{ - eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey); - os_free(peerkey); -} - - -static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst, - const u8 *peer, - u16 mui, u16 error_type, int ver) -{ - size_t rlen; - struct wpa_eapol_key *err; - struct wpa_eapol_key_192 *err192; - struct rsn_error_kde error; - u8 *rbuf, *pos; - size_t kde_len; - u16 key_info; - - kde_len = 2 + RSN_SELECTOR_LEN + sizeof(error); - if (peer) - kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN; - - rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, - NULL, sizeof(*err) + kde_len, &rlen, - (void *) &err); - if (rbuf == NULL) - return -1; - err192 = (struct wpa_eapol_key_192 *) err; - - err->type = EAPOL_KEY_TYPE_RSN; - key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_ERROR | - WPA_KEY_INFO_REQUEST; - WPA_PUT_BE16(err->key_info, key_info); - WPA_PUT_BE16(err->key_length, 0); - os_memcpy(err->replay_counter, sm->request_counter, - WPA_REPLAY_COUNTER_LEN); - inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); - - WPA_PUT_BE16(err->key_data_length, (u16) kde_len); - pos = (u8 *) (err + 1); - - if (peer) { - /* Peer MAC Address KDE */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN); - } - - /* Error KDE */ - error.mui = host_to_be16(mui); - error.error_type = host_to_be16(error_type); - wpa_add_kde(pos, RSN_KEY_DATA_ERROR, (u8 *) &error, sizeof(error)); - - if (peer) { - wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error (peer " - MACSTR " mui %d error_type %d)", - MAC2STR(peer), mui, error_type); - } else { - wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error " - "(mui %d error_type %d)", mui, error_type); - } - - wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, dst, - ETH_P_EAPOL, rbuf, rlen, err192->key_mic); - - return 0; -} - - -static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm, - const unsigned char *src_addr, - const struct wpa_eapol_key *key, - int ver, struct wpa_peerkey *peerkey) -{ - size_t rlen; - struct wpa_eapol_key *reply; - struct wpa_eapol_key_192 *reply192; - u8 *rbuf, *pos; - size_t kde_len; - u16 key_info; - - /* KDEs: Peer RSN IE, Initiator MAC Address, Initiator Nonce */ - kde_len = peerkey->rsnie_p_len + - 2 + RSN_SELECTOR_LEN + ETH_ALEN + - 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN; - - rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, - NULL, sizeof(*reply) + kde_len, &rlen, - (void *) &reply); - if (rbuf == NULL) - return -1; - reply192 = (struct wpa_eapol_key_192 *) reply; - - reply->type = EAPOL_KEY_TYPE_RSN; - key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_SECURE; - WPA_PUT_BE16(reply->key_info, key_info); - WPA_PUT_BE16(reply->key_length, 0); - os_memcpy(reply->replay_counter, key->replay_counter, - WPA_REPLAY_COUNTER_LEN); - - os_memcpy(reply->key_nonce, peerkey->pnonce, WPA_NONCE_LEN); - - WPA_PUT_BE16(reply->key_data_length, (u16) kde_len); - pos = (u8 *) (reply + 1); - - /* Peer RSN IE */ - pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len); - - /* Initiator MAC Address KDE */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peerkey->addr, ETH_ALEN); - - /* Initiator Nonce */ - wpa_add_kde(pos, RSN_KEY_DATA_NONCE, peerkey->inonce, WPA_NONCE_LEN); - - wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK M3"); - wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, src_addr, - ETH_P_EAPOL, rbuf, rlen, reply192->key_mic); - - return 0; -} - - -static int wpa_supplicant_process_smk_m2( - struct wpa_sm *sm, const unsigned char *src_addr, - const struct wpa_eapol_key *key, size_t extra_len, int ver) -{ - struct wpa_peerkey *peerkey; - struct wpa_eapol_ie_parse kde; - struct wpa_ie_data ie; - int cipher; - struct rsn_ie_hdr *hdr; - u8 *pos; - - wpa_printf(MSG_DEBUG, "RSN: Received SMK M2"); - - if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { - wpa_printf(MSG_INFO, "RSN: SMK handshake not allowed for " - "the current network"); - return -1; - } - - if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < - 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M2"); - return -1; - } - - if (kde.rsn_ie == NULL || kde.mac_addr == NULL || - kde.mac_addr_len < ETH_ALEN) { - wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " - "SMK M2"); - return -1; - } - - wpa_printf(MSG_DEBUG, "RSN: SMK M2 - SMK initiator " MACSTR, - MAC2STR(kde.mac_addr)); - - if (kde.rsn_ie_len > PEERKEY_MAX_IE_LEN) { - wpa_printf(MSG_INFO, "RSN: Too long Initiator RSN IE in SMK " - "M2"); - return -1; - } - - if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse RSN IE in SMK M2"); - return -1; - } - - cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher & - sm->allowed_pairwise_cipher, 0); - if (cipher < 0) { - wpa_printf(MSG_INFO, "RSN: No acceptable cipher in SMK M2"); - wpa_supplicant_send_smk_error(sm, src_addr, kde.mac_addr, - STK_MUI_SMK, STK_ERR_CPHR_NS, - ver); - return -1; - } - wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey", - wpa_cipher_txt(cipher)); - - /* TODO: find existing entry and if found, use that instead of adding - * a new one; how to handle the case where both ends initiate at the - * same time? */ - peerkey = os_zalloc(sizeof(*peerkey)); - if (peerkey == NULL) - return -1; - os_memcpy(peerkey->addr, kde.mac_addr, ETH_ALEN); - os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN); - os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len); - peerkey->rsnie_i_len = kde.rsn_ie_len; - peerkey->cipher = cipher; - peerkey->akmp = ie.key_mgmt; - - if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "WPA: Failed to get random data for PNonce"); - wpa_supplicant_peerkey_free(sm, peerkey); - return -1; - } - - hdr = (struct rsn_ie_hdr *) peerkey->rsnie_p; - hdr->elem_id = WLAN_EID_RSN; - WPA_PUT_LE16(hdr->version, RSN_VERSION); - pos = (u8 *) (hdr + 1); - /* Group Suite can be anything for SMK RSN IE; receiver will just - * ignore it. */ - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - pos += RSN_SELECTOR_LEN; - /* Include only the selected cipher in pairwise cipher suite */ - WPA_PUT_LE16(pos, 1); - pos += 2; - RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, cipher)); - pos += RSN_SELECTOR_LEN; - - hdr->len = (pos - peerkey->rsnie_p) - 2; - peerkey->rsnie_p_len = pos - peerkey->rsnie_p; - wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake", - peerkey->rsnie_p, peerkey->rsnie_p_len); - - wpa_supplicant_send_smk_m3(sm, src_addr, key, ver, peerkey); - - peerkey->next = sm->peerkey; - sm->peerkey = peerkey; - - return 0; -} - - -/** - * rsn_smkid - Derive SMK identifier - * @smk: Station master key (32 bytes) - * @pnonce: Peer Nonce - * @mac_p: Peer MAC address - * @inonce: Initiator Nonce - * @mac_i: Initiator MAC address - * @akmp: Negotiated AKM - * - * 8.5.1.4 Station to station (STK) key hierarchy - * SMKID = HMAC-SHA1-128(SMK, "SMK Name" || PNonce || MAC_P || INonce || MAC_I) - */ -static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p, - const u8 *inonce, const u8 *mac_i, u8 *smkid, - int akmp) -{ - char *title = "SMK Name"; - const u8 *addr[5]; - const size_t len[5] = { 8, WPA_NONCE_LEN, ETH_ALEN, WPA_NONCE_LEN, - ETH_ALEN }; - unsigned char hash[SHA256_MAC_LEN]; - - addr[0] = (u8 *) title; - addr[1] = pnonce; - addr[2] = mac_p; - addr[3] = inonce; - addr[4] = mac_i; - -#ifdef CONFIG_IEEE80211W - if (wpa_key_mgmt_sha256(akmp)) - hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash); - else -#endif /* CONFIG_IEEE80211W */ - hmac_sha1_vector(smk, PMK_LEN, 5, addr, len, hash); - os_memcpy(smkid, hash, PMKID_LEN); -} - - -static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, - struct wpa_peerkey *peerkey) -{ - size_t mlen; - struct wpa_eapol_key *msg; - u8 *mbuf; - size_t kde_len; - u16 key_info, ver; - - kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; - - mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, - sizeof(*msg) + kde_len, &mlen, - (void *) &msg); - if (mbuf == NULL) - return; - - msg->type = EAPOL_KEY_TYPE_RSN; - - if (peerkey->cipher != WPA_CIPHER_TKIP) - ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; - else - ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; - - key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK; - WPA_PUT_BE16(msg->key_info, key_info); - - if (peerkey->cipher != WPA_CIPHER_TKIP) - WPA_PUT_BE16(msg->key_length, 16); - else - WPA_PUT_BE16(msg->key_length, 32); - - os_memcpy(msg->replay_counter, peerkey->replay_counter, - WPA_REPLAY_COUNTER_LEN); - inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); - - WPA_PUT_BE16(msg->key_data_length, kde_len); - wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID, - peerkey->smkid, PMKID_LEN); - - if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "RSN: Failed to get random data for INonce (STK)"); - os_free(mbuf); - return; - } - wpa_hexdump(MSG_DEBUG, "RSN: INonce for STK 4-Way Handshake", - peerkey->inonce, WPA_NONCE_LEN); - os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN); - - wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR, - MAC2STR(peerkey->addr)); - wpa_eapol_key_send(sm, NULL, 0, ver, peerkey->addr, ETH_P_EAPOL, - mbuf, mlen, NULL); -} - - -static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm, - struct wpa_peerkey *peerkey) -{ - size_t mlen; - struct wpa_eapol_key *msg; - u8 *mbuf, *pos; - size_t kde_len; - u16 key_info, ver; - be32 lifetime; - - kde_len = peerkey->rsnie_i_len + - 2 + RSN_SELECTOR_LEN + sizeof(lifetime); - - mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, - sizeof(*msg) + kde_len, &mlen, - (void *) &msg); - if (mbuf == NULL) - return; - - msg->type = EAPOL_KEY_TYPE_RSN; - - if (peerkey->cipher != WPA_CIPHER_TKIP) - ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; - else - ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; - - key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK | - WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; - WPA_PUT_BE16(msg->key_info, key_info); - - if (peerkey->cipher != WPA_CIPHER_TKIP) - WPA_PUT_BE16(msg->key_length, 16); - else - WPA_PUT_BE16(msg->key_length, 32); - - os_memcpy(msg->replay_counter, peerkey->replay_counter, - WPA_REPLAY_COUNTER_LEN); - inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); - - WPA_PUT_BE16(msg->key_data_length, kde_len); - pos = (u8 *) (msg + 1); - pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len); - lifetime = host_to_be32(peerkey->lifetime); - wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, - (u8 *) &lifetime, sizeof(lifetime)); - - os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN); - - wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR, - MAC2STR(peerkey->addr)); - wpa_eapol_key_send(sm, peerkey->stk.kck, peerkey->stk.kck_len, ver, - peerkey->addr, ETH_P_EAPOL, mbuf, mlen, - msg->key_mic); -} - - -static int wpa_supplicant_process_smk_m4(struct wpa_peerkey *peerkey, - struct wpa_eapol_ie_parse *kde) -{ - wpa_printf(MSG_DEBUG, "RSN: Received SMK M4 (Initiator " MACSTR ")", - MAC2STR(kde->mac_addr)); - - if (os_memcmp(kde->smk + PMK_LEN, peerkey->pnonce, WPA_NONCE_LEN) != 0) - { - wpa_printf(MSG_INFO, "RSN: PNonce in SMK KDE does not " - "match with the one used in SMK M3"); - return -1; - } - - if (os_memcmp(kde->nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) { - wpa_printf(MSG_INFO, "RSN: INonce in SMK M4 did not " - "match with the one received in SMK M2"); - return -1; - } - - return 0; -} - - -static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm, - const unsigned char *src_addr, - const struct wpa_eapol_key *key, - int ver, - struct wpa_peerkey *peerkey, - struct wpa_eapol_ie_parse *kde) -{ - int cipher; - struct wpa_ie_data ie; - - wpa_printf(MSG_DEBUG, "RSN: Received SMK M5 (Peer " MACSTR ")", - MAC2STR(kde->mac_addr)); - if (kde->rsn_ie == NULL || kde->rsn_ie_len > PEERKEY_MAX_IE_LEN || - wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0) { - wpa_printf(MSG_INFO, "RSN: No RSN IE in SMK M5"); - /* TODO: abort negotiation */ - return -1; - } - - if (os_memcmp(key->key_nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) { - wpa_printf(MSG_INFO, "RSN: Key Nonce in SMK M5 does " - "not match with INonce used in SMK M1"); - return -1; - } - - if (os_memcmp(kde->smk + PMK_LEN, peerkey->inonce, WPA_NONCE_LEN) != 0) - { - wpa_printf(MSG_INFO, "RSN: INonce in SMK KDE does not " - "match with the one used in SMK M1"); - return -1; - } - - os_memcpy(peerkey->rsnie_p, kde->rsn_ie, kde->rsn_ie_len); - peerkey->rsnie_p_len = kde->rsn_ie_len; - os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN); - - cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher & - sm->allowed_pairwise_cipher, 0); - if (cipher < 0) { - wpa_printf(MSG_INFO, "RSN: SMK Peer STA " MACSTR " selected " - "unacceptable cipher", MAC2STR(kde->mac_addr)); - wpa_supplicant_send_smk_error(sm, src_addr, kde->mac_addr, - STK_MUI_SMK, STK_ERR_CPHR_NS, - ver); - /* TODO: abort negotiation */ - return -1; - } - wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey", - wpa_cipher_txt(cipher)); - peerkey->cipher = cipher; - - return 0; -} - - -static int wpa_supplicant_process_smk_m45( - struct wpa_sm *sm, const unsigned char *src_addr, - const struct wpa_eapol_key *key, size_t extra_len, int ver) -{ - struct wpa_peerkey *peerkey; - struct wpa_eapol_ie_parse kde; - u32 lifetime; - - if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { - wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for " - "the current network"); - return -1; - } - - if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < - 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M4/M5"); - return -1; - } - - if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || - kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN || - kde.smk == NULL || kde.smk_len < PMK_LEN + WPA_NONCE_LEN || - kde.lifetime == NULL || kde.lifetime_len < 4) { - wpa_printf(MSG_INFO, "RSN: No MAC Address, Nonce, SMK, or " - "Lifetime KDE in SMK M4/M5"); - return -1; - } - - for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { - if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == 0 && - os_memcmp(peerkey->initiator ? peerkey->inonce : - peerkey->pnonce, - key->key_nonce, WPA_NONCE_LEN) == 0) - break; - } - if (peerkey == NULL) { - wpa_printf(MSG_INFO, "RSN: No matching SMK handshake found " - "for SMK M4/M5: peer " MACSTR, - MAC2STR(kde.mac_addr)); - return -1; - } - - if (peerkey->initiator) { - if (wpa_supplicant_process_smk_m5(sm, src_addr, key, ver, - peerkey, &kde) < 0) - return -1; - } else { - if (wpa_supplicant_process_smk_m4(peerkey, &kde) < 0) - return -1; - } - - os_memcpy(peerkey->smk, kde.smk, PMK_LEN); - peerkey->smk_complete = 1; - wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", peerkey->smk, PMK_LEN); - lifetime = WPA_GET_BE32(kde.lifetime); - wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime); - if (lifetime > 1000000000) - lifetime = 1000000000; /* avoid overflowing eloop time */ - peerkey->lifetime = lifetime; - eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, - sm, peerkey); - - if (peerkey->initiator) { - rsn_smkid(peerkey->smk, peerkey->pnonce, peerkey->addr, - peerkey->inonce, sm->own_addr, peerkey->smkid, - peerkey->akmp); - wpa_supplicant_send_stk_1_of_4(sm, peerkey); - } else { - rsn_smkid(peerkey->smk, peerkey->pnonce, sm->own_addr, - peerkey->inonce, peerkey->addr, peerkey->smkid, - peerkey->akmp); - } - wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN); - - return 0; -} - - -static int wpa_supplicant_process_smk_error( - struct wpa_sm *sm, const unsigned char *src_addr, - const struct wpa_eapol_key *key, size_t extra_len) -{ - struct wpa_eapol_ie_parse kde; - struct rsn_error_kde error; - u8 peer[ETH_ALEN]; - u16 error_type; - - wpa_printf(MSG_DEBUG, "RSN: Received SMK Error"); - - if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { - wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for " - "the current network"); - return -1; - } - - if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < - 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); - return -1; - } - - if (kde.error == NULL || kde.error_len < sizeof(error)) { - wpa_printf(MSG_INFO, "RSN: No Error KDE in SMK Error"); - return -1; - } - - if (kde.mac_addr && kde.mac_addr_len >= ETH_ALEN) - os_memcpy(peer, kde.mac_addr, ETH_ALEN); - else - os_memset(peer, 0, ETH_ALEN); - os_memcpy(&error, kde.error, sizeof(error)); - error_type = be_to_host16(error.error_type); - wpa_msg(sm->ctx->msg_ctx, MSG_INFO, - "RSN: SMK Error KDE received: MUI %d error_type %d peer " - MACSTR, - be_to_host16(error.mui), error_type, - MAC2STR(peer)); - - if (kde.mac_addr && - (error_type == STK_ERR_STA_NR || error_type == STK_ERR_STA_NRSN || - error_type == STK_ERR_CPHR_NS)) { - struct wpa_peerkey *peerkey; - - for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { - if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == - 0) - break; - } - if (peerkey == NULL) { - wpa_printf(MSG_DEBUG, "RSN: No matching SMK handshake " - "found for SMK Error"); - return -1; - } - /* TODO: abort SMK/STK handshake and remove all related keys */ - } - - return 0; -} - - -static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm, - struct wpa_peerkey *peerkey, - const struct wpa_eapol_key *key, - u16 ver, const u8 *key_data, - size_t key_data_len) -{ - struct wpa_eapol_ie_parse ie; - size_t kde_buf_len; - struct wpa_ptk *stk; - u8 buf[8], *kde_buf, *pos; - be32 lifetime; - - wpa_printf(MSG_DEBUG, "RSN: RX message 1 of STK 4-Way Handshake from " - MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); - - os_memset(&ie, 0, sizeof(ie)); - - /* RSN: msg 1/4 should contain SMKID for the selected SMK */ - wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", key_data, key_data_len); - if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0 || - ie.pmkid == NULL) { - wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4"); - return; - } - if (os_memcmp_const(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) { - wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4", - ie.pmkid, PMKID_LEN); - return; - } - - if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "RSN: Failed to get random data for PNonce"); - return; - } - wpa_hexdump(MSG_DEBUG, "WPA: Renewed PNonce", - peerkey->pnonce, WPA_NONCE_LEN); - - /* Calculate STK which will be stored as a temporary STK until it has - * been verified when processing message 3/4. */ - stk = &peerkey->tstk; - wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion", - sm->own_addr, peerkey->addr, - peerkey->pnonce, key->key_nonce, - stk, peerkey->akmp, peerkey->cipher); - /* Supplicant: swap tx/rx Mic keys */ - os_memcpy(buf, &stk->tk[16], 8); - os_memcpy(&stk->tk[16], &stk->tk[24], 8); - os_memcpy(&stk->tk[24], buf, 8); - peerkey->tstk_set = 1; - - kde_buf_len = peerkey->rsnie_p_len + - 2 + RSN_SELECTOR_LEN + sizeof(lifetime) + - 2 + RSN_SELECTOR_LEN + PMKID_LEN; - kde_buf = os_malloc(kde_buf_len); - if (kde_buf == NULL) - return; - pos = kde_buf; - pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len); - lifetime = host_to_be32(peerkey->lifetime); - pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, - (u8 *) &lifetime, sizeof(lifetime)); - wpa_add_kde(pos, RSN_KEY_DATA_PMKID, peerkey->smkid, PMKID_LEN); - - if (wpa_supplicant_send_2_of_4(sm, peerkey->addr, key, ver, - peerkey->pnonce, kde_buf, kde_buf_len, - stk)) { - os_free(kde_buf); - return; - } - os_free(kde_buf); - - os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN); -} - - -static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm, - struct wpa_peerkey *peerkey, - struct wpa_eapol_ie_parse *kde) -{ - u32 lifetime; - - if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime)) - return; - - lifetime = WPA_GET_BE32(kde->lifetime); - - if (lifetime >= peerkey->lifetime) { - wpa_printf(MSG_DEBUG, "RSN: Peer used SMK lifetime %u seconds " - "which is larger than or equal to own value %u " - "seconds - ignored", lifetime, peerkey->lifetime); - return; - } - - wpa_printf(MSG_DEBUG, "RSN: Peer used shorter SMK lifetime %u seconds " - "(own was %u seconds) - updated", - lifetime, peerkey->lifetime); - peerkey->lifetime = lifetime; - - eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey); - eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, - sm, peerkey); -} - - -static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm, - struct wpa_peerkey *peerkey, - const struct wpa_eapol_key *key, - u16 ver, const u8 *key_data, - size_t key_data_len) -{ - struct wpa_eapol_ie_parse kde; - - wpa_printf(MSG_DEBUG, "RSN: RX message 2 of STK 4-Way Handshake from " - MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); - - os_memset(&kde, 0, sizeof(kde)); - - /* RSN: msg 2/4 should contain SMKID for the selected SMK and RSN IE - * from the peer. It may also include Lifetime KDE. */ - wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", key_data, key_data_len); - if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0 || - kde.pmkid == NULL || kde.rsn_ie == NULL) { - wpa_printf(MSG_DEBUG, "RSN: No SMKID or RSN IE in STK 2/4"); - return; - } - - if (os_memcmp_const(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) { - wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 2/4", - kde.pmkid, PMKID_LEN); - return; - } - - if (kde.rsn_ie_len != peerkey->rsnie_p_len || - os_memcmp(kde.rsn_ie, peerkey->rsnie_p, kde.rsn_ie_len) != 0) { - wpa_printf(MSG_INFO, "RSN: Peer RSN IE in SMK and STK " - "handshakes did not match"); - wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in SMK handshake", - peerkey->rsnie_p, peerkey->rsnie_p_len); - wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in STK handshake", - kde.rsn_ie, kde.rsn_ie_len); - return; - } - - wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde); - - wpa_supplicant_send_stk_3_of_4(sm, peerkey); - os_memcpy(peerkey->pnonce, key->key_nonce, WPA_NONCE_LEN); -} - - -static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm, - struct wpa_peerkey *peerkey, - const struct wpa_eapol_key *key, - u16 ver, const u8 *key_data, - size_t key_data_len) -{ - struct wpa_eapol_ie_parse kde; - size_t key_len; - const u8 *_key; - u8 key_buf[32], rsc[6]; - - wpa_printf(MSG_DEBUG, "RSN: RX message 3 of STK 4-Way Handshake from " - MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); - - os_memset(&kde, 0, sizeof(kde)); - - /* RSN: msg 3/4 should contain Initiator RSN IE. It may also include - * Lifetime KDE. */ - wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", key_data, key_data_len); - if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0) { - wpa_printf(MSG_DEBUG, "RSN: Failed to parse key data in " - "STK 3/4"); - return; - } - - if (kde.rsn_ie_len != peerkey->rsnie_i_len || - os_memcmp(kde.rsn_ie, peerkey->rsnie_i, kde.rsn_ie_len) != 0) { - wpa_printf(MSG_INFO, "RSN: Initiator RSN IE in SMK and STK " - "handshakes did not match"); - wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in SMK " - "handshake", - peerkey->rsnie_i, peerkey->rsnie_i_len); - wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in STK " - "handshake", - kde.rsn_ie, kde.rsn_ie_len); - return; - } - - if (os_memcmp(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN) != 0) { - wpa_printf(MSG_WARNING, "RSN: INonce from message 1 of STK " - "4-Way Handshake differs from 3 of STK 4-Way " - "Handshake - drop packet (src=" MACSTR ")", - MAC2STR(peerkey->addr)); - return; - } - - wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde); - - if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver, - WPA_GET_BE16(key->key_info), - &peerkey->stk)) - return; - - _key = peerkey->stk.tk; - if (peerkey->cipher == WPA_CIPHER_TKIP) { - /* Swap Tx/Rx keys for Michael MIC */ - os_memcpy(key_buf, _key, 16); - os_memcpy(key_buf + 16, _key + 24, 8); - os_memcpy(key_buf + 24, _key + 16, 8); - _key = key_buf; - key_len = 32; - } else - key_len = 16; - - os_memset(rsc, 0, 6); - if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1, - rsc, sizeof(rsc), _key, key_len) < 0) { - os_memset(key_buf, 0, sizeof(key_buf)); - wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the " - "driver."); - return; - } - os_memset(key_buf, 0, sizeof(key_buf)); -} - - -static void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm, - struct wpa_peerkey *peerkey, - const struct wpa_eapol_key *key, - u16 ver) -{ - u8 rsc[6]; - - wpa_printf(MSG_DEBUG, "RSN: RX message 4 of STK 4-Way Handshake from " - MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); - - os_memset(rsc, 0, 6); - if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1, - rsc, sizeof(rsc), peerkey->stk.tk, - peerkey->cipher == WPA_CIPHER_TKIP ? 32 : 16) < 0) { - wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the " - "driver."); - return; - } -} - - -/** - * peerkey_verify_eapol_key_mic - Verify PeerKey MIC - * @sm: Pointer to WPA state machine data from wpa_sm_init() - * @peerkey: Pointer to the PeerKey data for the peer - * @key: Pointer to the EAPOL-Key frame header - * @ver: Version bits from EAPOL-Key Key Info - * @buf: Pointer to the beginning of EAPOL-Key frame - * @len: Length of the EAPOL-Key frame - * Returns: 0 on success, -1 on failure - */ -int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, - struct wpa_peerkey *peerkey, - struct wpa_eapol_key_192 *key, u16 ver, - const u8 *buf, size_t len) -{ - u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; - size_t mic_len = 16; - int ok = 0; - - if (peerkey->initiator && !peerkey->stk_set) { - wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion", - sm->own_addr, peerkey->addr, - peerkey->inonce, key->key_nonce, - &peerkey->stk, peerkey->akmp, peerkey->cipher); - peerkey->stk_set = 1; - } - - os_memcpy(mic, key->key_mic, mic_len); - if (peerkey->tstk_set) { - os_memset(key->key_mic, 0, mic_len); - wpa_eapol_key_mic(peerkey->tstk.kck, peerkey->tstk.kck_len, - sm->key_mgmt, ver, buf, len, key->key_mic); - if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) { - wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC " - "when using TSTK - ignoring TSTK"); - } else { - ok = 1; - peerkey->tstk_set = 0; - peerkey->stk_set = 1; - os_memcpy(&peerkey->stk, &peerkey->tstk, - sizeof(peerkey->stk)); - os_memset(&peerkey->tstk, 0, sizeof(peerkey->tstk)); - } - } - - if (!ok && peerkey->stk_set) { - os_memset(key->key_mic, 0, mic_len); - wpa_eapol_key_mic(peerkey->stk.kck, peerkey->stk.kck_len, - sm->key_mgmt, ver, buf, len, key->key_mic); - if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) { - wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC " - "- dropping packet"); - return -1; - } - ok = 1; - } - - if (!ok) { - wpa_printf(MSG_WARNING, "RSN: Could not verify EAPOL-Key MIC " - "- dropping packet"); - return -1; - } - - os_memcpy(peerkey->replay_counter, key->replay_counter, - WPA_REPLAY_COUNTER_LEN); - peerkey->replay_counter_set = 1; - return 0; -} - - -/** - * wpa_sm_stkstart - Send EAPOL-Key Request for STK handshake (STK M1) - * @sm: Pointer to WPA state machine data from wpa_sm_init() - * @peer: MAC address of the peer STA - * Returns: 0 on success, or -1 on failure - * - * Send an EAPOL-Key Request to the current authenticator to start STK - * handshake with the peer. - */ -int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) -{ - size_t rlen, kde_len; - struct wpa_eapol_key *req; - int key_info, ver; - u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos; - u16 count; - struct rsn_ie_hdr *hdr; - struct wpa_peerkey *peerkey; - struct wpa_ie_data ie; - - if (sm->proto != WPA_PROTO_RSN || !sm->ptk_set || !sm->peerkey_enabled) - return -1; - - if (sm->ap_rsn_ie && - wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &ie) == 0 && - !(ie.capabilities & WPA_CAPABILITY_PEERKEY_ENABLED)) { - wpa_printf(MSG_DEBUG, "RSN: Current AP does not support STK"); - return -1; - } - - if (sm->pairwise_cipher != WPA_CIPHER_TKIP) - ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; - else - ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; - - if (wpa_sm_get_bssid(sm, bssid) < 0) { - wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key " - "SMK M1"); - return -1; - } - - /* TODO: find existing entry and if found, use that instead of adding - * a new one */ - peerkey = os_zalloc(sizeof(*peerkey)); - if (peerkey == NULL) - return -1; - peerkey->initiator = 1; - os_memcpy(peerkey->addr, peer, ETH_ALEN); - peerkey->akmp = sm->key_mgmt; - - /* SMK M1: - * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, - * MIC=MIC, DataKDs=(RSNIE_I, MAC_P KDE)) - */ - - hdr = (struct rsn_ie_hdr *) peerkey->rsnie_i; - hdr->elem_id = WLAN_EID_RSN; - WPA_PUT_LE16(hdr->version, RSN_VERSION); - pos = (u8 *) (hdr + 1); - /* Group Suite can be anything for SMK RSN IE; receiver will just - * ignore it. */ - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - pos += RSN_SELECTOR_LEN; - count_pos = pos; - pos += 2; - - count = rsn_cipher_put_suites(pos, sm->allowed_pairwise_cipher); - pos += count * RSN_SELECTOR_LEN; - WPA_PUT_LE16(count_pos, count); - - hdr->len = (pos - peerkey->rsnie_i) - 2; - peerkey->rsnie_i_len = pos - peerkey->rsnie_i; - wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake", - peerkey->rsnie_i, peerkey->rsnie_i_len); - - kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; - - rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, - sizeof(*req) + kde_len, &rlen, - (void *) &req); - if (rbuf == NULL) { - wpa_supplicant_peerkey_free(sm, peerkey); - return -1; - } - - req->type = EAPOL_KEY_TYPE_RSN; - key_info = WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_REQUEST | ver; - WPA_PUT_BE16(req->key_info, key_info); - WPA_PUT_BE16(req->key_length, 0); - os_memcpy(req->replay_counter, sm->request_counter, - WPA_REPLAY_COUNTER_LEN); - inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); - - if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "WPA: Failed to get random data for INonce"); - os_free(rbuf); - wpa_supplicant_peerkey_free(sm, peerkey); - return -1; - } - os_memcpy(req->key_nonce, peerkey->inonce, WPA_NONCE_LEN); - wpa_hexdump(MSG_DEBUG, "WPA: INonce for SMK handshake", - req->key_nonce, WPA_NONCE_LEN); - - WPA_PUT_BE16(req->key_data_length, (u16) kde_len); - pos = (u8 *) (req + 1); - - /* Initiator RSN IE */ - pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len); - /* Peer MAC address KDE */ - wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN); - - wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer " - MACSTR ")", MAC2STR(peer)); - wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid, - ETH_P_EAPOL, rbuf, rlen, req->key_mic); - - peerkey->next = sm->peerkey; - sm->peerkey = peerkey; - - return 0; -} - - -/** - * peerkey_deinit - Free PeerKey values - * @sm: Pointer to WPA state machine data from wpa_sm_init() - */ -void peerkey_deinit(struct wpa_sm *sm) -{ - struct wpa_peerkey *prev, *peerkey = sm->peerkey; - while (peerkey) { - prev = peerkey; - peerkey = peerkey->next; - wpa_supplicant_peerkey_free(sm, prev); - } - sm->peerkey = NULL; -} - - -void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey, - struct wpa_eapol_key *key, u16 key_info, u16 ver, - const u8 *key_data, size_t key_data_len) -{ - if ((key_info & (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) == - (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) { - /* 3/4 STK 4-Way Handshake */ - wpa_supplicant_process_stk_3_of_4(sm, peerkey, key, ver, - key_data, key_data_len); - } else if (key_info & WPA_KEY_INFO_ACK) { - /* 1/4 STK 4-Way Handshake */ - wpa_supplicant_process_stk_1_of_4(sm, peerkey, key, ver, - key_data, key_data_len); - } else if (key_info & WPA_KEY_INFO_SECURE) { - /* 4/4 STK 4-Way Handshake */ - wpa_supplicant_process_stk_4_of_4(sm, peerkey, key, ver); - } else { - /* 2/4 STK 4-Way Handshake */ - wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver, - key_data, key_data_len); - } -} - - -void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr, - struct wpa_eapol_key *key, size_t extra_len, - u16 key_info, u16 ver) -{ - if (key_info & WPA_KEY_INFO_ERROR) { - /* SMK Error */ - wpa_supplicant_process_smk_error(sm, src_addr, key, extra_len); - } else if (key_info & WPA_KEY_INFO_ACK) { - /* SMK M2 */ - wpa_supplicant_process_smk_m2(sm, src_addr, key, extra_len, - ver); - } else { - /* SMK M4 or M5 */ - wpa_supplicant_process_smk_m45(sm, src_addr, key, extra_len, - ver); - } -} - -#endif /* CONFIG_PEERKEY */ diff --git a/src/rsn_supp/peerkey.h b/src/rsn_supp/peerkey.h deleted file mode 100644 index 6ccd948baace..000000000000 --- a/src/rsn_supp/peerkey.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * WPA Supplicant - PeerKey for Direct Link Setup (DLS) - * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#ifndef PEERKEY_H -#define PEERKEY_H - -#define PEERKEY_MAX_IE_LEN 80 -struct wpa_peerkey { - struct wpa_peerkey *next; - int initiator; /* whether this end was initator for SMK handshake */ - u8 addr[ETH_ALEN]; /* other end MAC address */ - u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */ - u8 pnonce[WPA_NONCE_LEN]; /* Peer Nonce */ - u8 rsnie_i[PEERKEY_MAX_IE_LEN]; /* Initiator RSN IE */ - size_t rsnie_i_len; - u8 rsnie_p[PEERKEY_MAX_IE_LEN]; /* Peer RSN IE */ - size_t rsnie_p_len; - u8 smk[PMK_LEN]; - int smk_complete; - u8 smkid[PMKID_LEN]; - u32 lifetime; - int cipher; /* Selected cipher (WPA_CIPHER_*) */ - u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; - int replay_counter_set; - int akmp; - - struct wpa_ptk stk, tstk; - int stk_set, tstk_set; -}; - - -#ifdef CONFIG_PEERKEY - -int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, - struct wpa_peerkey *peerkey, - struct wpa_eapol_key_192 *key, u16 ver, - const u8 *buf, size_t len); -void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey, - struct wpa_eapol_key *key, u16 key_info, u16 ver, - const u8 *key_data, size_t key_data_len); -void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr, - struct wpa_eapol_key *key, size_t extra_len, - u16 key_info, u16 ver); -void peerkey_deinit(struct wpa_sm *sm); - -#else /* CONFIG_PEERKEY */ - -static inline int -peerkey_verify_eapol_key_mic(struct wpa_sm *sm, - struct wpa_peerkey *peerkey, - struct wpa_eapol_key *key, u16 ver, - const u8 *buf, size_t len) -{ - return -1; -} - -static inline void -peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey, - struct wpa_eapol_key *key, u16 key_info, u16 ver, - const u8 *key_data, size_t key_data_len) -{ -} - -static inline void -peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr, - struct wpa_eapol_key *key, size_t extra_len, - u16 key_info, u16 ver) -{ -} - -static inline void peerkey_deinit(struct wpa_sm *sm) -{ -} - -#endif /* CONFIG_PEERKEY */ - -#endif /* PEERKEY_H */ diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c index 3d8d12223c26..fdd522087dbc 100644 --- a/src/rsn_supp/pmksa_cache.c +++ b/src/rsn_supp/pmksa_cache.c @@ -43,7 +43,10 @@ static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry, enum pmksa_free_reason reason) { - wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid); + wpa_sm_remove_pmkid(pmksa->sm, entry->network_ctx, entry->aa, + entry->pmkid, + entry->fils_cache_id_set ? entry->fils_cache_id : + NULL); pmksa->pmksa_count--; pmksa->free_cb(entry, pmksa->ctx, reason); _pmksa_cache_free_entry(entry); @@ -93,7 +96,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa : - pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL); + pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, 0); if (entry) { sec = pmksa->pmksa->reauth_time - now.sec; if (sec < 0) @@ -116,6 +119,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) * @spa: Supplicant address * @network_ctx: Network configuration context for this PMK * @akmp: WPA_KEY_MGMT_* used in key derivation + * @cache_id: Pointer to FILS Cache Identifier or %NULL if not advertised * Returns: Pointer to the added PMKSA cache entry or %NULL on error * * This function create a PMKSA entry for a new PMK and adds it to the PMKSA @@ -126,9 +130,10 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *kck, size_t kck_len, - const u8 *aa, const u8 *spa, void *network_ctx, int akmp) + const u8 *aa, const u8 *spa, void *network_ctx, int akmp, + const u8 *cache_id) { - struct rsn_pmksa_cache_entry *entry, *pos, *prev; + struct rsn_pmksa_cache_entry *entry; struct os_reltime now; if (pmk_len > PMK_LEN_MAX) @@ -149,24 +154,38 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, else if (wpa_key_mgmt_suite_b(akmp)) rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); else - rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, - wpa_key_mgmt_sha256(akmp)); + rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp); os_get_reltime(&now); entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime; entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime * pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100; entry->akmp = akmp; + if (cache_id) { + entry->fils_cache_id_set = 1; + os_memcpy(entry->fils_cache_id, cache_id, FILS_CACHE_ID_LEN); + } os_memcpy(entry->aa, aa, ETH_ALEN); entry->network_ctx = network_ctx; + return pmksa_cache_add_entry(pmksa, entry); +} + + +struct rsn_pmksa_cache_entry * +pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ + struct rsn_pmksa_cache_entry *pos, *prev; + /* Replace an old entry for the same Authenticator (if found) with the * new entry */ pos = pmksa->pmksa; prev = NULL; while (pos) { - if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) { - if (pos->pmk_len == pmk_len && - os_memcmp_const(pos->pmk, pmk, pmk_len) == 0 && + if (os_memcmp(entry->aa, pos->aa, ETH_ALEN) == 0) { + if (pos->pmk_len == entry->pmk_len && + os_memcmp_const(pos->pmk, entry->pmk, + entry->pmk_len) == 0 && os_memcmp_const(pos->pmkid, entry->pmkid, PMKID_LEN) == 0) { wpa_printf(MSG_DEBUG, "WPA: reusing previous " @@ -192,8 +211,8 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, "the current AP and any PMKSA cache entry " "that was based on the old PMK"); if (!pos->opportunistic) - pmksa_cache_flush(pmksa, network_ctx, pos->pmk, - pos->pmk_len); + pmksa_cache_flush(pmksa, entry->network_ctx, + pos->pmk, pos->pmk_len); pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE); break; } @@ -244,8 +263,10 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, } pmksa->pmksa_count++; wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR - " network_ctx=%p", MAC2STR(entry->aa), network_ctx); - wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid); + " network_ctx=%p", MAC2STR(entry->aa), entry->network_ctx); + wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid, + entry->fils_cache_id_set ? entry->fils_cache_id : NULL, + entry->pmk, entry->pmk_len); return entry; } @@ -320,17 +341,20 @@ void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) * @aa: Authenticator address or %NULL to match any * @pmkid: PMKID or %NULL to match any * @network_ctx: Network context or %NULL to match any + * @akmp: Specific AKMP to search for or 0 for any * Returns: Pointer to PMKSA cache entry or %NULL if no match was found */ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, - const void *network_ctx) + const void *network_ctx, + int akmp) { struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; while (entry) { if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) && (pmkid == NULL || os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) && + (!akmp || akmp == entry->akmp) && (network_ctx == NULL || network_ctx == entry->network_ctx)) return entry; entry = entry->next; @@ -345,16 +369,19 @@ pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, const u8 *aa) { struct rsn_pmksa_cache_entry *new_entry; + os_time_t old_expiration = old_entry->expiration; new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len, NULL, NULL, 0, aa, pmksa->sm->own_addr, - old_entry->network_ctx, old_entry->akmp); + old_entry->network_ctx, old_entry->akmp, + old_entry->fils_cache_id_set ? + old_entry->fils_cache_id : NULL); if (new_entry == NULL) return NULL; /* TODO: reorder entries based on expiration time? */ - new_entry->expiration = old_entry->expiration; + new_entry->expiration = old_expiration; new_entry->opportunistic = 1; return new_entry; @@ -366,6 +393,7 @@ pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @network_ctx: Network configuration context * @aa: Authenticator address for the new AP + * @akmp: Specific AKMP to search for or 0 for any * Returns: Pointer to a new PMKSA cache entry or %NULL if not available * * Try to create a new PMKSA cache entry opportunistically by guessing that the @@ -374,7 +402,7 @@ pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, */ struct rsn_pmksa_cache_entry * pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, - const u8 *aa) + const u8 *aa, int akmp) { struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; @@ -382,7 +410,8 @@ pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, if (network_ctx == NULL) return NULL; while (entry) { - if (entry->network_ctx == network_ctx) { + if (entry->network_ctx == network_ctx && + (!akmp || entry->akmp == akmp)) { entry = pmksa_cache_clone_entry(pmksa, entry, aa); if (entry) { wpa_printf(MSG_DEBUG, "RSN: added " @@ -397,6 +426,24 @@ pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, } +static struct rsn_pmksa_cache_entry * +pmksa_cache_get_fils_cache_id(struct rsn_pmksa_cache *pmksa, + const void *network_ctx, const u8 *cache_id) +{ + struct rsn_pmksa_cache_entry *entry; + + for (entry = pmksa->pmksa; entry; entry = entry->next) { + if (network_ctx == entry->network_ctx && + entry->fils_cache_id_set && + os_memcmp(cache_id, entry->fils_cache_id, + FILS_CACHE_ID_LEN) == 0) + return entry; + } + + return NULL; +} + + /** * pmksa_cache_get_current - Get the current used PMKSA entry * @sm: Pointer to WPA state machine data from wpa_sm_init() @@ -429,33 +476,44 @@ void pmksa_cache_clear_current(struct wpa_sm *sm) * @bssid: BSSID for PMKSA or %NULL if not used * @network_ctx: Network configuration context * @try_opportunistic: Whether to allow opportunistic PMKSA caching + * @fils_cache_id: Pointer to FILS Cache Identifier or %NULL if not used * Returns: 0 if PMKSA was found or -1 if no matching entry was found */ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, const u8 *bssid, void *network_ctx, - int try_opportunistic) + int try_opportunistic, const u8 *fils_cache_id, + int akmp) { struct rsn_pmksa_cache *pmksa = sm->pmksa; wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p " - "try_opportunistic=%d", network_ctx, try_opportunistic); + "try_opportunistic=%d akmp=0x%x", + network_ctx, try_opportunistic, akmp); if (pmkid) wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID", pmkid, PMKID_LEN); if (bssid) wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR, MAC2STR(bssid)); + if (fils_cache_id) + wpa_printf(MSG_DEBUG, + "RSN: Search for FILS Cache Identifier %02x%02x", + fils_cache_id[0], fils_cache_id[1]); sm->cur_pmksa = NULL; if (pmkid) sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid, - network_ctx); + network_ctx, akmp); if (sm->cur_pmksa == NULL && bssid) sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL, - network_ctx); + network_ctx, akmp); if (sm->cur_pmksa == NULL && try_opportunistic && bssid) sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa, network_ctx, - bssid); + bssid, akmp); + if (sm->cur_pmksa == NULL && fils_cache_id) + sm->cur_pmksa = pmksa_cache_get_fils_cache_id(pmksa, + network_ctx, + fils_cache_id); if (sm->cur_pmksa) { wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID", sm->cur_pmksa->pmkid, PMKID_LEN); @@ -482,11 +540,20 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) char *pos = buf; struct rsn_pmksa_cache_entry *entry; struct os_reltime now; + int cache_id_used = 0; + + for (entry = pmksa->pmksa; entry; entry = entry->next) { + if (entry->fils_cache_id_set) { + cache_id_used = 1; + break; + } + } os_get_reltime(&now); ret = os_snprintf(pos, buf + len - pos, "Index / AA / PMKID / expiration (in seconds) / " - "opportunistic\n"); + "opportunistic%s\n", + cache_id_used ? " / FILS Cache Identifier" : ""); if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; @@ -501,18 +568,36 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) pos += ret; pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, PMKID_LEN); - ret = os_snprintf(pos, buf + len - pos, " %d %d\n", + ret = os_snprintf(pos, buf + len - pos, " %d %d", (int) (entry->expiration - now.sec), entry->opportunistic); if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; + if (entry->fils_cache_id_set) { + ret = os_snprintf(pos, buf + len - pos, " %02x%02x", + entry->fils_cache_id[0], + entry->fils_cache_id[1]); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + } + ret = os_snprintf(pos, buf + len - pos, "\n"); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; entry = entry->next; } return pos - buf; } +struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa) +{ + return pmksa->pmksa; +} + + /** * pmksa_cache_init - Initialize PMKSA cache * @free_cb: Callback function to be called when a PMKSA cache entry is freed diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h index daede6dac7fe..6c49fa9248fb 100644 --- a/src/rsn_supp/pmksa_cache.h +++ b/src/rsn_supp/pmksa_cache.h @@ -21,6 +21,14 @@ struct rsn_pmksa_cache_entry { int akmp; /* WPA_KEY_MGMT_* */ u8 aa[ETH_ALEN]; + /* + * If FILS Cache Identifier is included (fils_cache_id_set), this PMKSA + * cache entry is applicable to all BSSs (any BSSID/aa[]) that + * advertise the same FILS Cache Identifier within the same ESS. + */ + u8 fils_cache_id[2]; + unsigned int fils_cache_id_set:1; + os_time_t reauth_time; /** @@ -53,20 +61,27 @@ pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, - const void *network_ctx); + const void *network_ctx, + int akmp); int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); +struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa); struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *kck, size_t kck_len, - const u8 *aa, const u8 *spa, void *network_ctx, int akmp); + const u8 *aa, const u8 *spa, void *network_ctx, int akmp, + const u8 *cache_id); +struct rsn_pmksa_cache_entry * +pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry); struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm); void pmksa_cache_clear_current(struct wpa_sm *sm); int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, const u8 *bssid, void *network_ctx, - int try_opportunistic); + int try_opportunistic, const u8 *fils_cache_id, + int akmp); struct rsn_pmksa_cache_entry * pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, - void *network_ctx, const u8 *aa); + void *network_ctx, const u8 *aa, int akmp); void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, const u8 *pmk, size_t pmk_len); @@ -86,7 +101,7 @@ static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) static inline struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, - const void *network_ctx) + const void *network_ctx, int akmp) { return NULL; } @@ -104,9 +119,23 @@ static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, } static inline struct rsn_pmksa_cache_entry * +pmksa_cache_head(struct rsn_pmksa_cache *pmksa) +{ + return NULL; +} + +static inline struct rsn_pmksa_cache_entry * +pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ + return NULL; +} + +static inline struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *kck, size_t kck_len, - const u8 *aa, const u8 *spa, void *network_ctx, int akmp) + const u8 *aa, const u8 *spa, void *network_ctx, int akmp, + const u8 *cache_id) { return NULL; } @@ -118,7 +147,9 @@ static inline void pmksa_cache_clear_current(struct wpa_sm *sm) static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, const u8 *bssid, void *network_ctx, - int try_opportunistic) + int try_opportunistic, + const u8 *fils_cache_id, + int akmp) { return -1; } diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c index 4c9a4fb8b14c..d0c43f464e27 100644 --- a/src/rsn_supp/preauth.c +++ b/src/rsn_supp/preauth.c @@ -97,7 +97,7 @@ static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, NULL, 0, sm->preauth_bssid, sm->own_addr, sm->network_ctx, - WPA_KEY_MGMT_IEEE8021X); + WPA_KEY_MGMT_IEEE8021X, NULL); } else { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: failed to get master session key from " @@ -323,7 +323,7 @@ void rsn_preauth_candidate_process(struct wpa_sm *sm) dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates, struct rsn_pmksa_candidate, list) { struct rsn_pmksa_cache_entry *p = NULL; - p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL); + p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL, 0); if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 && (p == NULL || p->opportunistic)) { wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA " @@ -342,7 +342,8 @@ void rsn_preauth_candidate_process(struct wpa_sm *sm) /* Some drivers (e.g., NDIS) expect to get notified about the * PMKIDs again, so report the existing data now. */ if (p) { - wpa_sm_add_pmkid(sm, candidate->bssid, p->pmkid); + wpa_sm_add_pmkid(sm, NULL, candidate->bssid, p->pmkid, + NULL, p->pmk, p->pmk_len); } dl_list_del(&candidate->list); @@ -371,7 +372,7 @@ void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, if (sm->network_ctx && sm->proactive_key_caching) pmksa_cache_get_opportunistic(sm->pmksa, sm->network_ctx, - bssid); + bssid, 0); if (!preauth) { wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without " @@ -482,7 +483,7 @@ void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie)) return; - pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL); + pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL, 0); if (pmksa && (!pmksa->opportunistic || !(ie.capabilities & WPA_CAPABILITY_PREAUTH))) return; diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c index 9eb973860049..345b0c84d871 100644 --- a/src/rsn_supp/tdls.c +++ b/src/rsn_supp/tdls.c @@ -35,6 +35,7 @@ #define TDLS_TESTING_DECLINE_RESP BIT(9) #define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10) #define TDLS_TESTING_WRONG_MIC BIT(11) +#define TDLS_TESTING_DOUBLE_TPK_M2 BIT(12) unsigned int tdls_testing = 0; #endif /* CONFIG_TDLS_TESTING */ @@ -303,10 +304,9 @@ static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code, peer->sm_tmr.peer_capab = peer_capab; peer->sm_tmr.buf_len = msg_len; os_free(peer->sm_tmr.buf); - peer->sm_tmr.buf = os_malloc(msg_len); + peer->sm_tmr.buf = os_memdup(msg, msg_len); if (peer->sm_tmr.buf == NULL) return -1; - os_memcpy(peer->sm_tmr.buf, msg, msg_len); wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered " "(action_code=%u)", action_code); @@ -413,8 +413,9 @@ static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer, size_t len[2]; u8 data[3 * ETH_ALEN]; - /* IEEE Std 802.11z-2010 8.5.9.1: - * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce)) + /* IEEE Std 802.11-2016 12.7.9.2: + * TPK-Key-Input = Hash(min(SNonce, ANonce) || max(SNonce, ANonce)) + * Hash = SHA-256 for TDLS */ len[0] = WPA_NONCE_LEN; len[1] = WPA_NONCE_LEN; @@ -432,11 +433,8 @@ static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer, key_input, SHA256_MAC_LEN); /* - * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK", - * min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY) - * TODO: is N_KEY really included in KDF Context and if so, in which - * presentation format (little endian 16-bit?) is it used? It gets - * added by the KDF anyway.. + * TPK = KDF-Hash-Length(TPK-Key-Input, "TDLS PMK", + * min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID) */ if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) { @@ -1220,9 +1218,10 @@ skip_ies: #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_DIFF_BSSID) { + struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos; + wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in " "Link Identifier"); - struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos; wpa_tdls_linkid(sm, peer, l); l->bssid[5] ^= 0x01; pos += sizeof(*l); @@ -2126,6 +2125,13 @@ skip_add_peer: goto error; } +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_DOUBLE_TPK_M2) { + wpa_printf(MSG_INFO, "TDLS: Testing - Send another TPK M2"); + wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer); + } +#endif /* CONFIG_TDLS_TESTING */ + return 0; error: @@ -2153,11 +2159,11 @@ static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) eloop_register_timeout(lifetime, 0, wpa_tdls_tpk_timeout, sm, peer); #ifdef CONFIG_TDLS_TESTING - if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) { - wpa_printf(MSG_DEBUG, "TDLS: Testing - disable TPK " - "expiration"); - eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); - } + if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) { + wpa_printf(MSG_DEBUG, + "TDLS: Testing - disable TPK expiration"); + eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); + } #endif /* CONFIG_TDLS_TESTING */ } @@ -2912,14 +2918,14 @@ void wpa_tdls_disassoc(struct wpa_sm *sm) static int wpa_tdls_prohibited(struct ieee802_11_elems *elems) { /* bit 38 - TDLS Prohibited */ - return !!(elems->ext_capab[2 + 4] & 0x40); + return !!(elems->ext_capab[4] & 0x40); } static int wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems *elems) { /* bit 39 - TDLS Channel Switch Prohibited */ - return !!(elems->ext_capab[2 + 4] & 0x80); + return !!(elems->ext_capab[4] & 0x80); } @@ -2932,7 +2938,7 @@ void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len) if (ies == NULL || ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed || - elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) + elems.ext_capab == NULL || elems.ext_capab_len < 5) return; sm->tdls_prohibited = wpa_tdls_prohibited(&elems); @@ -2951,7 +2957,7 @@ void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len) if (ies == NULL || ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed || - elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) + elems.ext_capab == NULL || elems.ext_capab_len < 5) return; if (!sm->tdls_prohibited && wpa_tdls_prohibited(&elems)) { diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index dcd75272151f..e0c913074c63 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - WPA state machine and EAPOL-Key processing - * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> * Copyright(c) 2015 Intel Deutschland GmbH * * This software may be distributed under the terms of the BSD license. @@ -10,10 +10,17 @@ #include "includes.h" #include "common.h" +#include "crypto/aes.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" #include "crypto/random.h" +#include "crypto/aes_siv.h" +#include "crypto/sha256.h" +#include "crypto/sha384.h" +#include "crypto/sha512.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "eap_common/eap_defs.h" #include "eapol_supp/eapol_supp_sm.h" #include "wpa.h" #include "eloop.h" @@ -21,7 +28,6 @@ #include "pmksa_cache.h" #include "wpa_i.h" #include "wpa_ie.h" -#include "peerkey.h" static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -30,8 +36,7 @@ static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; /** * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message * @sm: Pointer to WPA state machine data from wpa_sm_init() - * @kck: Key Confirmation Key (KCK, part of PTK) - * @kck_len: KCK length in octets + * @ptk: PTK for Key Confirmation/Encryption Key * @ver: Version field from Key Info * @dest: Destination address for the frame * @proto: Ethertype (usually ETH_P_EAPOL) @@ -40,13 +45,16 @@ static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written * Returns: >= 0 on success, < 0 on failure */ -int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, +int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk, int ver, const u8 *dest, u16 proto, u8 *msg, size_t msg_len, u8 *key_mic) { int ret = -1; - size_t mic_len = wpa_mic_len(sm->key_mgmt); + size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); + wpa_printf(MSG_DEBUG, "WPA: Send EAPOL-Key frame to " MACSTR + " ver=%d mic_len=%d key_mgmt=0x%x", + MAC2STR(dest), ver, (int) mic_len, sm->key_mgmt); if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) { /* * Association event was not yet received; try to fetch @@ -64,16 +72,89 @@ int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, MAC2STR(dest)); } } - if (key_mic && - wpa_eapol_key_mic(kck, kck_len, sm->key_mgmt, ver, msg, msg_len, - key_mic)) { - wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, - "WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC", - ver, sm->key_mgmt); + + if (mic_len) { + if (key_mic && (!ptk || !ptk->kck_len)) + goto out; + + if (key_mic && + wpa_eapol_key_mic(ptk->kck, ptk->kck_len, sm->key_mgmt, ver, + msg, msg_len, key_mic)) { + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC", + ver, sm->key_mgmt); + goto out; + } + if (ptk) + wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", + ptk->kck, ptk->kck_len); + wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", + key_mic, mic_len); + } else { +#ifdef CONFIG_FILS + /* AEAD cipher - Key MIC field not used */ + struct ieee802_1x_hdr *s_hdr, *hdr; + struct wpa_eapol_key *s_key, *key; + u8 *buf, *s_key_data, *key_data; + size_t buf_len = msg_len + AES_BLOCK_SIZE; + size_t key_data_len; + u16 eapol_len; + const u8 *aad[1]; + size_t aad_len[1]; + + if (!ptk || !ptk->kek_len) + goto out; + + key_data_len = msg_len - sizeof(struct ieee802_1x_hdr) - + sizeof(struct wpa_eapol_key) - 2; + + buf = os_malloc(buf_len); + if (!buf) + goto out; + + os_memcpy(buf, msg, msg_len); + hdr = (struct ieee802_1x_hdr *) buf; + key = (struct wpa_eapol_key *) (hdr + 1); + key_data = ((u8 *) (key + 1)) + 2; + + /* Update EAPOL header to include AES-SIV overhead */ + eapol_len = be_to_host16(hdr->length); + eapol_len += AES_BLOCK_SIZE; + hdr->length = host_to_be16(eapol_len); + + /* Update Key Data Length field to include AES-SIV overhead */ + WPA_PUT_BE16((u8 *) (key + 1), AES_BLOCK_SIZE + key_data_len); + + s_hdr = (struct ieee802_1x_hdr *) msg; + s_key = (struct wpa_eapol_key *) (s_hdr + 1); + s_key_data = ((u8 *) (s_key + 1)) + 2; + + wpa_hexdump_key(MSG_DEBUG, "WPA: Plaintext Key Data", + s_key_data, key_data_len); + + wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", ptk->kek, ptk->kek_len); + /* AES-SIV AAD from EAPOL protocol version field (inclusive) to + * to Key Data (exclusive). */ + aad[0] = buf; + aad_len[0] = key_data - buf; + if (aes_siv_encrypt(ptk->kek, ptk->kek_len, + s_key_data, key_data_len, + 1, aad, aad_len, key_data) < 0) { + os_free(buf); + goto out; + } + + wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV", + key_data, AES_BLOCK_SIZE + key_data_len); + + os_free(msg); + msg = buf; + msg_len = buf_len; +#else /* CONFIG_FILS */ goto out; +#endif /* CONFIG_FILS */ } - wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len); - wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len); + wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len); ret = wpa_sm_ether_send(sm, dest, proto, msg, msg_len); eapol_sm_notify_tx_eapol_key(sm->eapol); @@ -97,12 +178,10 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) { size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; - struct wpa_eapol_key_192 *reply192; int key_info, ver; - u8 bssid[ETH_ALEN], *rbuf, *key_mic; + u8 bssid[ETH_ALEN], *rbuf, *key_mic, *mic; - if (sm->key_mgmt == WPA_KEY_MGMT_OSEN || - wpa_key_mgmt_suite_b(sm->key_mgmt)) + if (wpa_use_akm_defined(sm->key_mgmt)) ver = WPA_KEY_INFO_TYPE_AKM_DEFINED; else if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt)) @@ -118,20 +197,21 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) return; } - mic_len = wpa_mic_len(sm->key_mgmt); - hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); + mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); + hdrlen = sizeof(*reply) + mic_len + 2; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen, &rlen, (void *) &reply); if (rbuf == NULL) return; - reply192 = (struct wpa_eapol_key_192 *) reply; reply->type = (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info = WPA_KEY_INFO_REQUEST | ver; if (sm->ptk_set) - key_info |= WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; + key_info |= WPA_KEY_INFO_SECURE; + if (sm->ptk_set && mic_len) + key_info |= WPA_KEY_INFO_MIC; if (error) key_info |= WPA_KEY_INFO_ERROR; if (pairwise) @@ -142,21 +222,19 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) WPA_REPLAY_COUNTER_LEN); inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); - if (mic_len == 24) - WPA_PUT_BE16(reply192->key_data_length, 0); - else - WPA_PUT_BE16(reply->key_data_length, 0); + mic = (u8 *) (reply + 1); + WPA_PUT_BE16(mic + mic_len, 0); if (!(key_info & WPA_KEY_INFO_MIC)) key_mic = NULL; else - key_mic = reply192->key_mic; /* same offset in reply */ + key_mic = mic; wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Sending EAPOL-Key Request (error=%d " "pairwise=%d ptk_set=%d len=%lu)", error, pairwise, sm->ptk_set, (unsigned long) rlen); - wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid, - ETH_P_EAPOL, rbuf, rlen, key_mic); + wpa_eapol_key_send(sm, &sm->ptk, ver, bssid, ETH_P_EAPOL, rbuf, rlen, + key_mic); } @@ -190,7 +268,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, * event before receiving this 1/4 message, so try to find a * matching PMKSA cache entry here. */ sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid, - NULL); + NULL, 0); if (sm->cur_pmksa) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: found matching PMKID from PMKSA cache"); @@ -210,11 +288,23 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, eapol_sm_notify_cached(sm->eapol); #ifdef CONFIG_IEEE80211R sm->xxkey_len = 0; +#ifdef CONFIG_SAE + if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE && + sm->pmk_len == PMK_LEN) { + /* Need to allow FT key derivation to proceed with + * PMK from SAE being used as the XXKey in cases where + * the PMKID in msg 1/4 matches the PMKSA entry that was + * just added based on SAE authentication for the + * initial mobility domain association. */ + os_memcpy(sm->xxkey, sm->pmk, sm->pmk_len); + sm->xxkey_len = sm->pmk_len; + } +#endif /* CONFIG_SAE */ #endif /* CONFIG_IEEE80211R */ } else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) { int res, pmk_len; - if (sm->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + if (wpa_key_mgmt_sha384(sm->key_mgmt)) pmk_len = PMK_LEN_SUITE_B_192; else pmk_len = PMK_LEN; @@ -233,14 +323,28 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, u8 buf[2 * PMK_LEN]; if (eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0) { - os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN); - sm->xxkey_len = PMK_LEN; + if (wpa_key_mgmt_sha384(sm->key_mgmt)) { + os_memcpy(sm->xxkey, buf, + SHA384_MAC_LEN); + sm->xxkey_len = SHA384_MAC_LEN; + } else { + os_memcpy(sm->xxkey, buf + PMK_LEN, + PMK_LEN); + sm->xxkey_len = PMK_LEN; + } os_memset(buf, 0, sizeof(buf)); } #endif /* CONFIG_IEEE80211R */ } if (res == 0) { struct rsn_pmksa_cache_entry *sa = NULL; + const u8 *fils_cache_id = NULL; + +#ifdef CONFIG_FILS + if (sm->fils_cache_id_set) + fils_cache_id = sm->fils_cache_id; +#endif /* CONFIG_FILS */ + wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state " "machines", sm->pmk, pmk_len); sm->pmk_len = pmk_len; @@ -253,11 +357,12 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, NULL, 0, src_addr, sm->own_addr, sm->network_ctx, - sm->key_mgmt); + sm->key_mgmt, + fils_cache_id); } if (!sm->cur_pmksa && pmkid && - pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL)) - { + pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL, + 0)) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: the new PMK matches with the " "PMKID"); @@ -341,9 +446,9 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, { size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; - struct wpa_eapol_key_192 *reply192; u8 *rbuf, *key_mic; u8 *rsn_ie_buf = NULL; + u16 key_info; if (wpa_ie == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - " @@ -383,8 +488,8 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len); - mic_len = wpa_mic_len(sm->key_mgmt); - hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); + mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); + hdrlen = sizeof(*reply) + mic_len + 2; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen + wpa_ie_len, &rlen, (void *) &reply); @@ -392,13 +497,16 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, os_free(rsn_ie_buf); return -1; } - reply192 = (struct wpa_eapol_key_192 *) reply; reply->type = (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; - WPA_PUT_BE16(reply->key_info, - ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC); + key_info = ver | WPA_KEY_INFO_KEY_TYPE; + if (mic_len) + key_info |= WPA_KEY_INFO_MIC; + else + key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; + WPA_PUT_BE16(reply->key_info, key_info); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) WPA_PUT_BE16(reply->key_length, 0); else @@ -408,21 +516,16 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter, WPA_REPLAY_COUNTER_LEN); - key_mic = reply192->key_mic; /* same offset for reply and reply192 */ - if (mic_len == 24) { - WPA_PUT_BE16(reply192->key_data_length, wpa_ie_len); - os_memcpy(reply192 + 1, wpa_ie, wpa_ie_len); - } else { - WPA_PUT_BE16(reply->key_data_length, wpa_ie_len); - os_memcpy(reply + 1, wpa_ie, wpa_ie_len); - } + key_mic = (u8 *) (reply + 1); + WPA_PUT_BE16(key_mic + mic_len, wpa_ie_len); /* Key Data Length */ + os_memcpy(key_mic + mic_len + 2, wpa_ie, wpa_ie_len); /* Key Data */ os_free(rsn_ie_buf); os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4"); - return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, - ETH_P_EAPOL, rbuf, rlen, key_mic); + return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen, + key_mic); } @@ -500,7 +603,8 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, /* Calculate PTK which will be stored as a temporary PTK until it has * been verified when processing message 3/4. */ ptk = &sm->tptk; - wpa_derive_ptk(sm, src_addr, key, ptk); + if (wpa_derive_ptk(sm, src_addr, key, ptk) < 0) + goto failed; if (sm->pairwise_cipher == WPA_CIPHER_TKIP) { u8 buf[8]; /* Supplicant: swap tx/rx Mic keys */ @@ -571,7 +675,9 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, sm, addr, MLME_SETPROTECTION_PROTECT_TYPE_RX_TX, MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); eapol_sm_notify_portValid(sm->eapol, TRUE); - if (wpa_key_mgmt_wpa_psk(sm->key_mgmt)) + if (wpa_key_mgmt_wpa_psk(sm->key_mgmt) || + sm->key_mgmt == WPA_KEY_MGMT_DPP || + sm->key_mgmt == WPA_KEY_MGMT_OWE) eapol_sm_notify_eap_success(sm->eapol, TRUE); /* * Start preauthentication after a short wait to avoid a @@ -638,6 +744,11 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, alg = wpa_cipher_to_alg(sm->pairwise_cipher); keylen = wpa_cipher_key_len(sm->pairwise_cipher); + if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) { + wpa_printf(MSG_DEBUG, "WPA: TK length mismatch: %d != %lu", + keylen, (long unsigned int) sm->ptk.tk_len); + return -1; + } rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { @@ -658,6 +769,7 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, /* TK is not needed anymore in supplicant */ os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN); + sm->ptk.tk_len = 0; sm->ptk.installed = 1; if (sm->wpa_ptk_rekey) { @@ -895,7 +1007,7 @@ static int wpa_supplicant_install_igtk(struct wpa_sm *sm, } wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, - "WPA: IGTK keyid %d pn %02x%02x%02x%02x%02x%02x", + "WPA: IGTK keyid %d pn " COMPACT_MACSTR, keyidx, MAC2STR(igtk->pn)); wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", igtk->igtk, len); if (keyidx > 4095) { @@ -1203,22 +1315,24 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, { size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; - struct wpa_eapol_key_192 *reply192; u8 *rbuf, *key_mic; - mic_len = wpa_mic_len(sm->key_mgmt); - hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); + mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); + hdrlen = sizeof(*reply) + mic_len + 2; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen, &rlen, (void *) &reply); if (rbuf == NULL) return -1; - reply192 = (struct wpa_eapol_key_192 *) reply; reply->type = (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info &= WPA_KEY_INFO_SECURE; - key_info |= ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC; + key_info |= ver | WPA_KEY_INFO_KEY_TYPE; + if (mic_len) + key_info |= WPA_KEY_INFO_MIC; + else + key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; WPA_PUT_BE16(reply->key_info, key_info); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) WPA_PUT_BE16(reply->key_length, 0); @@ -1227,15 +1341,12 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, os_memcpy(reply->replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); - key_mic = reply192->key_mic; /* same offset for reply and reply192 */ - if (mic_len == 24) - WPA_PUT_BE16(reply192->key_data_length, 0); - else - WPA_PUT_BE16(reply->key_data_length, 0); + key_mic = (u8 *) (reply + 1); + WPA_PUT_BE16(key_mic + mic_len, 0); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); - return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, - ETH_P_EAPOL, rbuf, rlen, key_mic); + return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen, + key_mic); } @@ -1350,13 +1461,19 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, if (ie.gtk) wpa_sm_set_rekey_offload(sm); - if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt)) { + /* Add PMKSA cache entry for Suite B AKMs here since PMKID can be + * calculated only after KCK has been derived. Though, do not replace an + * existing PMKSA entry after each 4-way handshake (i.e., new KCK/PMKID) + * to avoid unnecessary changes of PMKID while continuing to use the + * same PMK. */ + if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt) && + !sm->cur_pmksa) { struct rsn_pmksa_cache_entry *sa; sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL, sm->ptk.kck, sm->ptk.kck_len, sm->bssid, sm->own_addr, - sm->network_ctx, sm->key_mgmt); + sm->network_ctx, sm->key_mgmt, NULL); if (!sm->cur_pmksa) sm->cur_pmksa = sa; } @@ -1378,7 +1495,8 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, int maxkeylen; struct wpa_eapol_ie_parse ie; - wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen); + wpa_hexdump_key(MSG_DEBUG, "RSN: msg 1/2 key data", + keydata, keydatalen); if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0) return -1; if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { @@ -1512,22 +1630,24 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, { size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; - struct wpa_eapol_key_192 *reply192; u8 *rbuf, *key_mic; - mic_len = wpa_mic_len(sm->key_mgmt); - hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); + mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); + hdrlen = sizeof(*reply) + mic_len + 2; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen, &rlen, (void *) &reply); if (rbuf == NULL) return -1; - reply192 = (struct wpa_eapol_key_192 *) reply; reply->type = (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info &= WPA_KEY_INFO_KEY_INDEX_MASK; - key_info |= ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; + key_info |= ver | WPA_KEY_INFO_SECURE; + if (mic_len) + key_info |= WPA_KEY_INFO_MIC; + else + key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; WPA_PUT_BE16(reply->key_info, key_info); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) WPA_PUT_BE16(reply->key_length, 0); @@ -1536,15 +1656,12 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, os_memcpy(reply->replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); - key_mic = reply192->key_mic; /* same offset for reply and reply192 */ - if (mic_len == 24) - WPA_PUT_BE16(reply192->key_data_length, 0); - else - WPA_PUT_BE16(reply->key_data_length, 0); + key_mic = (u8 *) (reply + 1); + WPA_PUT_BE16(key_mic + mic_len, 0); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); - return wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, - sm->bssid, ETH_P_EAPOL, rbuf, rlen, key_mic); + return wpa_eapol_key_send(sm, &sm->ptk, ver, sm->bssid, ETH_P_EAPOL, + rbuf, rlen, key_mic); } @@ -1559,7 +1676,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, struct wpa_gtk_data gd; const u8 *key_rsc; - if (!sm->msg_3_of_4_ok) { + if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group Key Handshake started prior to completion of 4-way handshake"); goto failed; @@ -1620,20 +1737,21 @@ failed: static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, - struct wpa_eapol_key_192 *key, + struct wpa_eapol_key *key, u16 ver, const u8 *buf, size_t len) { u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; int ok = 0; - size_t mic_len = wpa_mic_len(sm->key_mgmt); + size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); - os_memcpy(mic, key->key_mic, mic_len); + os_memcpy(mic, key + 1, mic_len); if (sm->tptk_set) { - os_memset(key->key_mic, 0, mic_len); - wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, sm->key_mgmt, - ver, buf, len, key->key_mic); - if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) { + os_memset(key + 1, 0, mic_len); + if (wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, + sm->key_mgmt, + ver, buf, len, (u8 *) (key + 1)) < 0 || + os_memcmp_const(mic, key + 1, mic_len) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid EAPOL-Key MIC " "when using TPTK - ignoring TPTK"); @@ -1643,14 +1761,23 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, sm->ptk_set = 1; os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk)); os_memset(&sm->tptk, 0, sizeof(sm->tptk)); + /* + * This assures the same TPTK in sm->tptk can never be + * copied twice to sm->ptk as the new PTK. In + * combination with the installed flag in the wpa_ptk + * struct, this assures the same PTK is only installed + * once. + */ + sm->renew_snonce = 1; } } if (!ok && sm->ptk_set) { - os_memset(key->key_mic, 0, mic_len); - wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, sm->key_mgmt, - ver, buf, len, key->key_mic); - if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) { + os_memset(key + 1, 0, mic_len); + if (wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, + sm->key_mgmt, + ver, buf, len, (u8 *) (key + 1)) < 0 || + os_memcmp_const(mic, key + 1, mic_len) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid EAPOL-Key MIC - " "dropping packet"); @@ -1675,7 +1802,8 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, /* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, - struct wpa_eapol_key *key, u16 ver, + struct wpa_eapol_key *key, + size_t mic_len, u16 ver, u8 *key_data, size_t *key_data_len) { wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data", @@ -1696,6 +1824,8 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, return -1; #else /* CONFIG_NO_RC4 */ u8 ek[32]; + + wpa_printf(MSG_DEBUG, "WPA: Decrypt Key Data using RC4"); os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len); if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) { @@ -1708,9 +1838,12 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, #endif /* CONFIG_NO_RC4 */ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || ver == WPA_KEY_INFO_TYPE_AES_128_CMAC || - sm->key_mgmt == WPA_KEY_MGMT_OSEN || - wpa_key_mgmt_suite_b(sm->key_mgmt)) { + wpa_use_aes_key_wrap(sm->key_mgmt)) { u8 *buf; + + wpa_printf(MSG_DEBUG, + "WPA: Decrypt Key Data using AES-UNWRAP (KEK length %u)", + (unsigned int) sm->ptk.kek_len); if (*key_data_len < 8 || *key_data_len % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported AES-WRAP len %u", @@ -1734,7 +1867,7 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, } os_memcpy(key_data, buf, *key_data_len); bin_clear_free(buf, *key_data_len); - WPA_PUT_BE16(key->key_data_length, *key_data_len); + WPA_PUT_BE16(((u8 *) (key + 1)) + mic_len, *key_data_len); } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported key_info type %d", ver); @@ -1797,6 +1930,76 @@ static void wpa_eapol_key_dump(struct wpa_sm *sm, } +#ifdef CONFIG_FILS +static int wpa_supp_aead_decrypt(struct wpa_sm *sm, u8 *buf, size_t buf_len, + size_t *key_data_len) +{ + struct wpa_ptk *ptk; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + u8 *pos, *tmp; + const u8 *aad[1]; + size_t aad_len[1]; + + if (*key_data_len < AES_BLOCK_SIZE) { + wpa_printf(MSG_INFO, "No room for AES-SIV data in the frame"); + return -1; + } + + if (sm->tptk_set) + ptk = &sm->tptk; + else if (sm->ptk_set) + ptk = &sm->ptk; + else + return -1; + + hdr = (struct ieee802_1x_hdr *) buf; + key = (struct wpa_eapol_key *) (hdr + 1); + pos = (u8 *) (key + 1); + pos += 2; /* Pointing at the Encrypted Key Data field */ + + tmp = os_malloc(*key_data_len); + if (!tmp) + return -1; + + /* AES-SIV AAD from EAPOL protocol version field (inclusive) to + * to Key Data (exclusive). */ + aad[0] = buf; + aad_len[0] = pos - buf; + if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, *key_data_len, + 1, aad, aad_len, tmp) < 0) { + wpa_printf(MSG_INFO, "Invalid AES-SIV data in the frame"); + bin_clear_free(tmp, *key_data_len); + return -1; + } + + /* AEAD decryption and validation completed successfully */ + (*key_data_len) -= AES_BLOCK_SIZE; + wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data", + tmp, *key_data_len); + + /* Replace Key Data field with the decrypted version */ + os_memcpy(pos, tmp, *key_data_len); + pos -= 2; /* Key Data Length field */ + WPA_PUT_BE16(pos, *key_data_len); + bin_clear_free(tmp, *key_data_len); + + if (sm->tptk_set) { + sm->tptk_set = 0; + sm->ptk_set = 1; + os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk)); + os_memset(&sm->tptk, 0, sizeof(sm->tptk)); + } + + os_memcpy(sm->rx_replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + sm->rx_replay_counter_set = 1; + + return 0; +} +#endif /* CONFIG_FILS */ + + /** * wpa_sm_rx_eapol - Process received WPA EAPOL frames * @sm: Pointer to WPA state machine data from wpa_sm_init() @@ -1819,20 +2022,18 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, size_t plen, data_len, key_data_len; const struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; - struct wpa_eapol_key_192 *key192; u16 key_info, ver; u8 *tmp = NULL; int ret = -1; - struct wpa_peerkey *peerkey = NULL; - u8 *key_data; + u8 *mic, *key_data; size_t mic_len, keyhdrlen; #ifdef CONFIG_IEEE80211R sm->ft_completed = 0; #endif /* CONFIG_IEEE80211R */ - mic_len = wpa_mic_len(sm->key_mgmt); - keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); + mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); + keyhdrlen = sizeof(*key) + mic_len + 2; if (len < sizeof(*hdr) + keyhdrlen) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, @@ -1879,17 +2080,12 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, * Make a copy of the frame since we need to modify the buffer during * MAC validation and Key Data decryption. */ - tmp = os_malloc(data_len); + tmp = os_memdup(buf, data_len); if (tmp == NULL) goto out; - os_memcpy(tmp, buf, data_len); key = (struct wpa_eapol_key *) (tmp + sizeof(struct ieee802_1x_hdr)); - key192 = (struct wpa_eapol_key_192 *) - (tmp + sizeof(struct ieee802_1x_hdr)); - if (mic_len == 24) - key_data = (u8 *) (key192 + 1); - else - key_data = (u8 *) (key + 1); + mic = (u8 *) (key + 1); + key_data = mic + mic_len + 2; if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN) { @@ -1900,11 +2096,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, goto out; } - if (mic_len == 24) - key_data_len = WPA_GET_BE16(key192->key_data_length); - else - key_data_len = WPA_GET_BE16(key->key_data_length); - wpa_eapol_key_dump(sm, key, key_data_len, key192->key_mic, mic_len); + key_data_len = WPA_GET_BE16(mic + mic_len); + wpa_eapol_key_dump(sm, key, key_data_len, mic, mic_len); if (key_data_len > plen - keyhdrlen) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key " @@ -1922,23 +2115,14 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES && - !wpa_key_mgmt_suite_b(sm->key_mgmt) && - sm->key_mgmt != WPA_KEY_MGMT_OSEN) { + !wpa_use_akm_defined(sm->key_mgmt)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor version %d", ver); goto out; } - if (sm->key_mgmt == WPA_KEY_MGMT_OSEN && - ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { - wpa_msg(sm->ctx->msg_ctx, MSG_INFO, - "OSEN: Unsupported EAPOL-Key descriptor version %d", - ver); - goto out; - } - - if (wpa_key_mgmt_suite_b(sm->key_mgmt) && + if (wpa_use_akm_defined(sm->key_mgmt) && ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)", @@ -1949,7 +2133,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) { /* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */ - if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { + if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && + !wpa_use_akm_defined(sm->key_mgmt)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "FT: AP did not use AES-128-CMAC"); goto out; @@ -1959,8 +2144,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, #ifdef CONFIG_IEEE80211W if (wpa_key_mgmt_sha256(sm->key_mgmt)) { if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && - sm->key_mgmt != WPA_KEY_MGMT_OSEN && - !wpa_key_mgmt_suite_b(sm->key_mgmt)) { + !wpa_use_akm_defined(sm->key_mgmt)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: AP did not use the " "negotiated AES-128-CMAC"); @@ -1969,7 +2153,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, } else #endif /* CONFIG_IEEE80211W */ if (sm->pairwise_cipher == WPA_CIPHER_CCMP && - !wpa_key_mgmt_suite_b(sm->key_mgmt) && + !wpa_use_akm_defined(sm->key_mgmt) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: CCMP is used, but EAPOL-Key " @@ -1989,7 +2173,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, } else goto out; } else if (sm->pairwise_cipher == WPA_CIPHER_GCMP && - !wpa_key_mgmt_suite_b(sm->key_mgmt) && + !wpa_use_akm_defined(sm->key_mgmt) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: GCMP is used, but EAPOL-Key " @@ -1997,44 +2181,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, goto out; } -#ifdef CONFIG_PEERKEY - for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { - if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0) - break; - } - - if (!(key_info & WPA_KEY_INFO_SMK_MESSAGE) && peerkey) { - if (!peerkey->initiator && peerkey->replay_counter_set && - os_memcmp(key->replay_counter, peerkey->replay_counter, - WPA_REPLAY_COUNTER_LEN) <= 0) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "RSN: EAPOL-Key Replay Counter did not " - "increase (STK) - dropping packet"); - goto out; - } else if (peerkey->initiator) { - u8 _tmp[WPA_REPLAY_COUNTER_LEN]; - os_memcpy(_tmp, key->replay_counter, - WPA_REPLAY_COUNTER_LEN); - inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN); - if (os_memcmp(_tmp, peerkey->replay_counter, - WPA_REPLAY_COUNTER_LEN) != 0) { - wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, - "RSN: EAPOL-Key Replay " - "Counter did not match (STK) - " - "dropping packet"); - goto out; - } - } - } - - if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) { - wpa_msg(sm->ctx->msg_ctx, MSG_INFO, - "RSN: Ack bit in key_info from STK peer"); - goto out; - } -#endif /* CONFIG_PEERKEY */ - - if (!peerkey && sm->rx_replay_counter_set && + if (sm->rx_replay_counter_set && os_memcmp(key->replay_counter, sm->rx_replay_counter, WPA_REPLAY_COUNTER_LEN) <= 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -2043,11 +2190,13 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, goto out; } - if (!(key_info & (WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE)) -#ifdef CONFIG_PEERKEY - && (peerkey == NULL || !peerkey->initiator) -#endif /* CONFIG_PEERKEY */ - ) { + if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Unsupported SMK bit in key_info"); + goto out; + } + + if (!(key_info & WPA_KEY_INFO_ACK)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: No Ack bit in key_info"); goto out; @@ -2059,19 +2208,19 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, goto out; } - if ((key_info & WPA_KEY_INFO_MIC) && !peerkey && - wpa_supplicant_verify_eapol_key_mic(sm, key192, ver, tmp, data_len)) + if ((key_info & WPA_KEY_INFO_MIC) && + wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len)) goto out; -#ifdef CONFIG_PEERKEY - if ((key_info & WPA_KEY_INFO_MIC) && peerkey && - peerkey_verify_eapol_key_mic(sm, peerkey, key192, ver, tmp, - data_len)) - goto out; -#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_FILS + if (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + if (wpa_supp_aead_decrypt(sm, tmp, data_len, &key_data_len)) + goto out; + } +#endif /* CONFIG_FILS */ if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) && - (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && mic_len) { /* * Only decrypt the Key Data field if the frame's authenticity * was verified. When using AES-SIV (FILS), the MIC flag is not @@ -2083,7 +2232,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, "WPA: Ignore EAPOL-Key with encrypted but unauthenticated data"); goto out; } - if (wpa_supplicant_decrypt_key_data(sm, key, ver, key_data, + if (wpa_supplicant_decrypt_key_data(sm, key, mic_len, + ver, key_data, &key_data_len)) goto out; } @@ -2095,11 +2245,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, "non-zero key index"); goto out; } - if (peerkey) { - /* PeerKey 4-Way Handshake */ - peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver, - key_data, key_data_len); - } else if (key_info & WPA_KEY_INFO_MIC) { + if (key_info & (WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ENCR_KEY_DATA)) { /* 3/4 4-Way Handshake */ wpa_supplicant_process_3_of_4(sm, key, ver, key_data, key_data_len); @@ -2109,19 +2256,16 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, ver, key_data, key_data_len); } - } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { - /* PeerKey SMK Handshake */ - peerkey_rx_eapol_smk(sm, src_addr, key, key_data_len, key_info, - ver); } else { - if (key_info & WPA_KEY_INFO_MIC) { + if ((mic_len && (key_info & WPA_KEY_INFO_MIC)) || + (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA))) { /* 1/2 Group Key Handshake */ wpa_supplicant_process_1_of_2(sm, src_addr, key, key_data, key_data_len, ver); } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "WPA: EAPOL-Key (Group) without Mic bit - " + "WPA: EAPOL-Key (Group) without Mic/Encr bit - " "dropped"); } } @@ -2296,6 +2440,7 @@ static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, } if (deauth) { + sm->pmk_len = 0; os_memset(sm->pmk, 0, sizeof(sm->pmk)); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } @@ -2353,13 +2498,21 @@ void wpa_sm_deinit(struct wpa_sm *sm) os_free(sm->ap_rsn_ie); wpa_sm_drop_sa(sm); os_free(sm->ctx); - peerkey_deinit(sm); #ifdef CONFIG_IEEE80211R os_free(sm->assoc_resp_ies); #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_TESTING_OPTIONS wpabuf_free(sm->test_assoc_ie); #endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_FILS_SK_PFS + crypto_ecdh_deinit(sm->fils_ecdh); +#endif /* CONFIG_FILS_SK_PFS */ +#ifdef CONFIG_FILS + wpabuf_free(sm->fils_ft_ies); +#endif /* CONFIG_FILS */ +#ifdef CONFIG_OWE + crypto_ecdh_deinit(sm->owe_ecdh); +#endif /* CONFIG_OWE */ os_free(sm); } @@ -2403,6 +2556,16 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) clear_keys = 0; } #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_FILS + if (sm->fils_completed) { + /* + * Clear portValid to kick EAPOL state machine to re-enter + * AUTHENTICATED state to get the EAPOL port Authorized. + */ + wpa_supplicant_key_neg_complete(sm, sm->bssid, 1); + clear_keys = 0; + } +#endif /* CONFIG_FILS */ if (clear_keys) { /* @@ -2443,7 +2606,6 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) { eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL); eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); - peerkey_deinit(sm); rsn_preauth_deinit(sm); pmksa_cache_clear_current(sm); if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE) @@ -2451,6 +2613,9 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) #ifdef CONFIG_TDLS wpa_tdls_disassoc(sm); #endif /* CONFIG_TDLS */ +#ifdef CONFIG_FILS + sm->fils_completed = 0; +#endif /* CONFIG_FILS */ #ifdef CONFIG_IEEE80211R sm->ft_reassoc_completed = 0; #endif /* CONFIG_IEEE80211R */ @@ -2459,6 +2624,7 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) wpa_sm_drop_sa(sm); sm->msg_3_of_4_ok = 0; + os_memset(sm->bssid, 0, ETH_ALEN); } @@ -2478,6 +2644,8 @@ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, if (sm == NULL) return; + wpa_hexdump_key(MSG_DEBUG, "WPA: Set PMK based on external data", + pmk, pmk_len); sm->pmk_len = pmk_len; os_memcpy(sm->pmk, pmk, pmk_len); @@ -2490,7 +2658,7 @@ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, if (bssid) { pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0, bssid, sm->own_addr, - sm->network_ctx, sm->key_mgmt); + sm->network_ctx, sm->key_mgmt, NULL); } } @@ -2508,11 +2676,15 @@ void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm) return; if (sm->cur_pmksa) { + wpa_hexdump_key(MSG_DEBUG, + "WPA: Set PMK based on current PMKSA", + sm->cur_pmksa->pmk, sm->cur_pmksa->pmk_len); sm->pmk_len = sm->cur_pmksa->pmk_len; os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len); } else { - sm->pmk_len = PMK_LEN; - os_memset(sm->pmk, 0, PMK_LEN); + wpa_printf(MSG_DEBUG, "WPA: No current PMKSA - clear PMK"); + sm->pmk_len = 0; + os_memset(sm->pmk, 0, PMK_LEN_MAX); } } @@ -2560,7 +2732,6 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) if (config) { sm->network_ctx = config->network_ctx; - sm->peerkey_enabled = config->peerkey_enabled; sm->allowed_pairwise_cipher = config->allowed_pairwise_cipher; sm->proactive_key_caching = config->proactive_key_caching; sm->eap_workaround = config->eap_workaround; @@ -2573,9 +2744,17 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) sm->wpa_ptk_rekey = config->wpa_ptk_rekey; sm->p2p = config->p2p; sm->wpa_rsc_relaxation = config->wpa_rsc_relaxation; +#ifdef CONFIG_FILS + if (config->fils_cache_id) { + sm->fils_cache_id_set = 1; + os_memcpy(sm->fils_cache_id, config->fils_cache_id, + FILS_CACHE_ID_LEN); + } else { + sm->fils_cache_id_set = 0; + } +#endif /* CONFIG_FILS */ } else { sm->network_ctx = NULL; - sm->peerkey_enabled = 0; sm->allowed_pairwise_cipher = 0; sm->proactive_key_caching = 0; sm->eap_workaround = 0; @@ -2728,9 +2907,12 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, >= 0 && rsn.capabilities & (WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC)) { - ret = os_snprintf(pos, end - pos, "pmf=%d\n", + ret = os_snprintf(pos, end - pos, "pmf=%d\n" + "mgmt_group_cipher=%s\n", (rsn.capabilities & - WPA_CAPABILITY_MFPR) ? 2 : 1); + WPA_CAPABILITY_MFPR) ? 2 : 1, + wpa_cipher_txt( + sm->mgmt_group_cipher)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -2796,12 +2978,15 @@ int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, * the correct version of the IE even if PMKSA caching is * aborted (which would remove PMKID from IE generation). */ - sm->assoc_wpa_ie = os_malloc(*wpa_ie_len); + sm->assoc_wpa_ie = os_memdup(wpa_ie, *wpa_ie_len); if (sm->assoc_wpa_ie == NULL) return -1; - os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len); sm->assoc_wpa_ie_len = *wpa_ie_len; + } else { + wpa_hexdump(MSG_DEBUG, + "WPA: Leave previously set WPA IE default", + sm->assoc_wpa_ie, sm->assoc_wpa_ie_len); } return 0; @@ -2832,11 +3017,10 @@ int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) sm->assoc_wpa_ie_len = 0; } else { wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len); - sm->assoc_wpa_ie = os_malloc(len); + sm->assoc_wpa_ie = os_memdup(ie, len); if (sm->assoc_wpa_ie == NULL) return -1; - os_memcpy(sm->assoc_wpa_ie, ie, len); sm->assoc_wpa_ie_len = len; } @@ -2867,11 +3051,10 @@ int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) sm->ap_wpa_ie_len = 0; } else { wpa_hexdump(MSG_DEBUG, "WPA: set AP WPA IE", ie, len); - sm->ap_wpa_ie = os_malloc(len); + sm->ap_wpa_ie = os_memdup(ie, len); if (sm->ap_wpa_ie == NULL) return -1; - os_memcpy(sm->ap_wpa_ie, ie, len); sm->ap_wpa_ie_len = len; } @@ -2902,11 +3085,10 @@ int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len) sm->ap_rsn_ie_len = 0; } else { wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len); - sm->ap_rsn_ie = os_malloc(len); + sm->ap_rsn_ie = os_memdup(ie, len); if (sm->ap_rsn_ie == NULL) return -1; - os_memcpy(sm->ap_rsn_ie, ie, len); sm->ap_rsn_ie_len = len; } @@ -2945,11 +3127,43 @@ int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) } +struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_head(struct wpa_sm *sm) +{ + return pmksa_cache_head(sm->pmksa); +} + + +struct rsn_pmksa_cache_entry * +wpa_sm_pmksa_cache_add_entry(struct wpa_sm *sm, + struct rsn_pmksa_cache_entry * entry) +{ + return pmksa_cache_add_entry(sm->pmksa, entry); +} + + +void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, + const u8 *pmkid, const u8 *bssid, + const u8 *fils_cache_id) +{ + sm->cur_pmksa = pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0, + bssid, sm->own_addr, sm->network_ctx, + sm->key_mgmt, fils_cache_id); +} + + +int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid, + const void *network_ctx) +{ + return pmksa_cache_get(sm->pmksa, bssid, NULL, network_ctx, 0) != NULL; +} + + void wpa_sm_drop_sa(struct wpa_sm *sm) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK"); sm->ptk_set = 0; sm->tptk_set = 0; + sm->pmk_len = 0; os_memset(sm->pmk, 0, sizeof(sm->pmk)); os_memset(&sm->ptk, 0, sizeof(sm->ptk)); os_memset(&sm->tptk, 0, sizeof(sm->tptk)); @@ -2961,8 +3175,11 @@ void wpa_sm_drop_sa(struct wpa_sm *sm) #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R os_memset(sm->xxkey, 0, sizeof(sm->xxkey)); + sm->xxkey_len = 0; os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0)); + sm->pmk_r0_len = 0; os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1)); + sm->pmk_r1_len = 0; #endif /* CONFIG_IEEE80211R */ } @@ -3047,27 +3264,6 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) #endif /* CONFIG_WNM */ -#ifdef CONFIG_PEERKEY -int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, - const u8 *buf, size_t len) -{ - struct wpa_peerkey *peerkey; - - for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { - if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0) - break; - } - - if (!peerkey) - return 0; - - wpa_sm_rx_eapol(sm, src_addr, buf, len); - - return 1; -} -#endif /* CONFIG_PEERKEY */ - - #ifdef CONFIG_P2P int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf) @@ -3112,9 +3308,1130 @@ void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, #ifdef CONFIG_TESTING_OPTIONS + void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf) { wpabuf_free(sm->test_assoc_ie); sm->test_assoc_ie = buf; } + + +const u8 * wpa_sm_get_anonce(struct wpa_sm *sm) +{ + return sm->anonce; +} + #endif /* CONFIG_TESTING_OPTIONS */ + + +unsigned int wpa_sm_get_key_mgmt(struct wpa_sm *sm) +{ + return sm->key_mgmt; +} + + +#ifdef CONFIG_FILS + +struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md) +{ + struct wpabuf *buf = NULL; + struct wpabuf *erp_msg; + struct wpabuf *pub = NULL; + + erp_msg = eapol_sm_build_erp_reauth_start(sm->eapol); + if (!erp_msg && !sm->cur_pmksa) { + wpa_printf(MSG_DEBUG, + "FILS: Neither ERP EAP-Initiate/Re-auth nor PMKSA cache entry is available - skip FILS"); + goto fail; + } + + wpa_printf(MSG_DEBUG, "FILS: Try to use FILS (erp=%d pmksa_cache=%d)", + erp_msg != NULL, sm->cur_pmksa != NULL); + + sm->fils_completed = 0; + + if (!sm->assoc_wpa_ie) { + wpa_printf(MSG_INFO, "FILS: No own RSN IE set for FILS"); + goto fail; + } + + if (random_get_bytes(sm->fils_nonce, FILS_NONCE_LEN) < 0 || + random_get_bytes(sm->fils_session, FILS_SESSION_LEN) < 0) + goto fail; + + wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Nonce", + sm->fils_nonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Session", + sm->fils_session, FILS_SESSION_LEN); + +#ifdef CONFIG_FILS_SK_PFS + sm->fils_dh_group = dh_group; + if (dh_group) { + crypto_ecdh_deinit(sm->fils_ecdh); + sm->fils_ecdh = crypto_ecdh_init(dh_group); + if (!sm->fils_ecdh) { + wpa_printf(MSG_INFO, + "FILS: Could not initialize ECDH with group %d", + dh_group); + goto fail; + } + pub = crypto_ecdh_get_pubkey(sm->fils_ecdh, 1); + if (!pub) + goto fail; + wpa_hexdump_buf(MSG_DEBUG, "FILS: Element (DH public key)", + pub); + sm->fils_dh_elem_len = wpabuf_len(pub); + } +#endif /* CONFIG_FILS_SK_PFS */ + + buf = wpabuf_alloc(1000 + sm->assoc_wpa_ie_len + + (pub ? wpabuf_len(pub) : 0)); + if (!buf) + goto fail; + + /* Fields following the Authentication algorithm number field */ + + /* Authentication Transaction seq# */ + wpabuf_put_le16(buf, 1); + + /* Status Code */ + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + + /* TODO: FILS PK */ +#ifdef CONFIG_FILS_SK_PFS + if (dh_group) { + /* Finite Cyclic Group */ + wpabuf_put_le16(buf, dh_group); + /* Element */ + wpabuf_put_buf(buf, pub); + } +#endif /* CONFIG_FILS_SK_PFS */ + + /* RSNE */ + wpa_hexdump(MSG_DEBUG, "FILS: RSNE in FILS Authentication frame", + sm->assoc_wpa_ie, sm->assoc_wpa_ie_len); + wpabuf_put_data(buf, sm->assoc_wpa_ie, sm->assoc_wpa_ie_len); + + if (md) { + /* MDE when using FILS for FT initial association */ + struct rsn_mdie *mdie; + + wpabuf_put_u8(buf, WLAN_EID_MOBILITY_DOMAIN); + wpabuf_put_u8(buf, sizeof(*mdie)); + mdie = wpabuf_put(buf, sizeof(*mdie)); + os_memcpy(mdie->mobility_domain, md, MOBILITY_DOMAIN_ID_LEN); + mdie->ft_capab = 0; + } + + /* FILS Nonce */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE); + wpabuf_put_data(buf, sm->fils_nonce, FILS_NONCE_LEN); + + /* FILS Session */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION); + wpabuf_put_data(buf, sm->fils_session, FILS_SESSION_LEN); + + /* FILS Wrapped Data */ + sm->fils_erp_pmkid_set = 0; + if (erp_msg) { + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(buf, 1 + wpabuf_len(erp_msg)); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_WRAPPED_DATA); + wpabuf_put_buf(buf, erp_msg); + /* Calculate pending PMKID here so that we do not need to + * maintain a copy of the EAP-Initiate/Reauth message. */ + if (fils_pmkid_erp(sm->key_mgmt, wpabuf_head(erp_msg), + wpabuf_len(erp_msg), + sm->fils_erp_pmkid) == 0) + sm->fils_erp_pmkid_set = 1; + } + + wpa_hexdump_buf(MSG_DEBUG, "RSN: FILS fields for Authentication frame", + buf); + +fail: + wpabuf_free(erp_msg); + wpabuf_free(pub); + return buf; +} + + +int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data, + size_t len) +{ + const u8 *pos, *end; + struct ieee802_11_elems elems; + struct wpa_ie_data rsn; + int pmkid_match = 0; + u8 ick[FILS_ICK_MAX_LEN]; + size_t ick_len; + int res; + struct wpabuf *dh_ss = NULL; + const u8 *g_sta = NULL; + size_t g_sta_len = 0; + const u8 *g_ap = NULL; + size_t g_ap_len = 0; + struct wpabuf *pub = NULL; + + os_memcpy(sm->bssid, bssid, ETH_ALEN); + + wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields", + data, len); + pos = data; + end = data + len; + + /* TODO: FILS PK */ +#ifdef CONFIG_FILS_SK_PFS + if (sm->fils_dh_group) { + u16 group; + + /* Using FILS PFS */ + + /* Finite Cyclic Group */ + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, + "FILS: No room for Finite Cyclic Group"); + goto fail; + } + group = WPA_GET_LE16(pos); + pos += 2; + if (group != sm->fils_dh_group) { + wpa_printf(MSG_DEBUG, + "FILS: Unexpected change in Finite Cyclic Group: %u (expected %u)", + group, sm->fils_dh_group); + goto fail; + } + + /* Element */ + if ((size_t) (end - pos) < sm->fils_dh_elem_len) { + wpa_printf(MSG_DEBUG, "FILS: No room for Element"); + goto fail; + } + + if (!sm->fils_ecdh) { + wpa_printf(MSG_DEBUG, "FILS: No ECDH state available"); + goto fail; + } + dh_ss = crypto_ecdh_set_peerkey(sm->fils_ecdh, 1, pos, + sm->fils_dh_elem_len); + if (!dh_ss) { + wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed"); + goto fail; + } + wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", dh_ss); + g_ap = pos; + g_ap_len = sm->fils_dh_elem_len; + pos += sm->fils_dh_elem_len; + } +#endif /* CONFIG_FILS_SK_PFS */ + + wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos); + if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, "FILS: Could not parse elements"); + goto fail; + } + + /* RSNE */ + wpa_hexdump(MSG_DEBUG, "FILS: RSN element", elems.rsn_ie, + elems.rsn_ie_len); + if (!elems.rsn_ie || + wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsn) < 0) { + wpa_printf(MSG_DEBUG, "FILS: No RSN element"); + goto fail; + } + + if (!elems.fils_nonce) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field"); + goto fail; + } + os_memcpy(sm->fils_anonce, elems.fils_nonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, FILS_NONCE_LEN); + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt)) { + struct wpa_ft_ies parse; + + if (!elems.mdie || !elems.ftie) { + wpa_printf(MSG_DEBUG, "FILS+FT: No MDE or FTE"); + goto fail; + } + + if (wpa_ft_parse_ies(pos, end - pos, &parse, + wpa_key_mgmt_sha384(sm->key_mgmt)) < 0) { + wpa_printf(MSG_DEBUG, "FILS+FT: Failed to parse IEs"); + goto fail; + } + + if (!parse.r0kh_id) { + wpa_printf(MSG_DEBUG, + "FILS+FT: No R0KH-ID subelem in FTE"); + goto fail; + } + os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len); + sm->r0kh_id_len = parse.r0kh_id_len; + wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID", + sm->r0kh_id, sm->r0kh_id_len); + + if (!parse.r1kh_id) { + wpa_printf(MSG_DEBUG, + "FILS+FT: No R1KH-ID subelem in FTE"); + goto fail; + } + os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN); + wpa_hexdump(MSG_DEBUG, "FILS+FT: R1KH-ID", + sm->r1kh_id, FT_R1KH_ID_LEN); + + /* TODO: Check MDE and FTE payload */ + + wpabuf_free(sm->fils_ft_ies); + sm->fils_ft_ies = wpabuf_alloc(2 + elems.mdie_len + + 2 + elems.ftie_len); + if (!sm->fils_ft_ies) + goto fail; + wpabuf_put_data(sm->fils_ft_ies, elems.mdie - 2, + 2 + elems.mdie_len); + wpabuf_put_data(sm->fils_ft_ies, elems.ftie - 2, + 2 + elems.ftie_len); + } else { + wpabuf_free(sm->fils_ft_ies); + sm->fils_ft_ies = NULL; + } +#endif /* CONFIG_IEEE80211R */ + + /* PMKID List */ + if (rsn.pmkid && rsn.num_pmkid > 0) { + wpa_hexdump(MSG_DEBUG, "FILS: PMKID List", + rsn.pmkid, rsn.num_pmkid * PMKID_LEN); + + if (rsn.num_pmkid != 1) { + wpa_printf(MSG_DEBUG, "FILS: Invalid PMKID selection"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "FILS: PMKID", rsn.pmkid, PMKID_LEN); + if (os_memcmp(sm->cur_pmksa->pmkid, rsn.pmkid, PMKID_LEN) != 0) + { + wpa_printf(MSG_DEBUG, "FILS: PMKID mismatch"); + wpa_hexdump(MSG_DEBUG, "FILS: Expected PMKID", + sm->cur_pmksa->pmkid, PMKID_LEN); + goto fail; + } + wpa_printf(MSG_DEBUG, + "FILS: Matching PMKID - continue using PMKSA caching"); + pmkid_match = 1; + } + if (!pmkid_match && sm->cur_pmksa) { + wpa_printf(MSG_DEBUG, + "FILS: No PMKID match - cannot use cached PMKSA entry"); + sm->cur_pmksa = NULL; + } + + /* FILS Session */ + if (!elems.fils_session) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Session element"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session, + FILS_SESSION_LEN); + if (os_memcmp(sm->fils_session, elems.fils_session, FILS_SESSION_LEN) + != 0) { + wpa_printf(MSG_DEBUG, "FILS: Session mismatch"); + wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session", + sm->fils_session, FILS_SESSION_LEN); + goto fail; + } + + /* FILS Wrapped Data */ + if (!sm->cur_pmksa && elems.fils_wrapped_data) { + u8 rmsk[ERP_MAX_KEY_LEN]; + size_t rmsk_len; + + wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data", + elems.fils_wrapped_data, + elems.fils_wrapped_data_len); + eapol_sm_process_erp_finish(sm->eapol, elems.fils_wrapped_data, + elems.fils_wrapped_data_len); + if (eapol_sm_failed(sm->eapol)) + goto fail; + + rmsk_len = ERP_MAX_KEY_LEN; + res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len); + if (res == PMK_LEN) { + rmsk_len = PMK_LEN; + res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len); + } + if (res) + goto fail; + + res = fils_rmsk_to_pmk(sm->key_mgmt, rmsk, rmsk_len, + sm->fils_nonce, sm->fils_anonce, + dh_ss ? wpabuf_head(dh_ss) : NULL, + dh_ss ? wpabuf_len(dh_ss) : 0, + sm->pmk, &sm->pmk_len); + os_memset(rmsk, 0, sizeof(rmsk)); + + /* Don't use DHss in PTK derivation if PMKSA caching is not + * used. */ + wpabuf_clear_free(dh_ss); + dh_ss = NULL; + + if (res) + goto fail; + + if (!sm->fils_erp_pmkid_set) { + wpa_printf(MSG_DEBUG, "FILS: PMKID not available"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "FILS: PMKID", sm->fils_erp_pmkid, + PMKID_LEN); + wpa_printf(MSG_DEBUG, "FILS: ERP processing succeeded - add PMKSA cache entry for the result"); + sm->cur_pmksa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, + sm->fils_erp_pmkid, NULL, 0, + sm->bssid, sm->own_addr, + sm->network_ctx, sm->key_mgmt, + NULL); + } + + if (!sm->cur_pmksa) { + wpa_printf(MSG_DEBUG, + "FILS: No remaining options to continue FILS authentication"); + goto fail; + } + + if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid, + sm->fils_nonce, sm->fils_anonce, + dh_ss ? wpabuf_head(dh_ss) : NULL, + dh_ss ? wpabuf_len(dh_ss) : 0, + &sm->ptk, ick, &ick_len, + sm->key_mgmt, sm->pairwise_cipher, + sm->fils_ft, &sm->fils_ft_len) < 0) { + wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK"); + goto fail; + } + + wpabuf_clear_free(dh_ss); + dh_ss = NULL; + + sm->ptk_set = 1; + sm->tptk_set = 0; + os_memset(&sm->tptk, 0, sizeof(sm->tptk)); + +#ifdef CONFIG_FILS_SK_PFS + if (sm->fils_dh_group) { + if (!sm->fils_ecdh) { + wpa_printf(MSG_INFO, "FILS: ECDH not initialized"); + goto fail; + } + pub = crypto_ecdh_get_pubkey(sm->fils_ecdh, 1); + if (!pub) + goto fail; + wpa_hexdump_buf(MSG_DEBUG, "FILS: gSTA", pub); + g_sta = wpabuf_head(pub); + g_sta_len = wpabuf_len(pub); + if (!g_ap) { + wpa_printf(MSG_INFO, "FILS: gAP not available"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "FILS: gAP", g_ap, g_ap_len); + } +#endif /* CONFIG_FILS_SK_PFS */ + + res = fils_key_auth_sk(ick, ick_len, sm->fils_nonce, + sm->fils_anonce, sm->own_addr, sm->bssid, + g_sta, g_sta_len, g_ap, g_ap_len, + sm->key_mgmt, sm->fils_key_auth_sta, + sm->fils_key_auth_ap, + &sm->fils_key_auth_len); + wpabuf_free(pub); + os_memset(ick, 0, sizeof(ick)); + return res; +fail: + wpabuf_free(pub); + wpabuf_clear_free(dh_ss); + return -1; +} + + +#ifdef CONFIG_IEEE80211R +static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf) +{ + struct rsn_ie_hdr *rsnie; + u16 capab; + u8 *pos; + int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt); + + /* RSNIE[PMKR0Name/PMKR1Name] */ + rsnie = wpabuf_put(buf, sizeof(*rsnie)); + rsnie->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(rsnie->version, RSN_VERSION); + + /* Group Suite Selector */ + if (!wpa_cipher_valid_group(sm->group_cipher)) { + wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)", + sm->group_cipher); + return -1; + } + pos = wpabuf_put(buf, RSN_SELECTOR_LEN); + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + sm->group_cipher)); + + /* Pairwise Suite Count */ + wpabuf_put_le16(buf, 1); + + /* Pairwise Suite List */ + if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { + wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)", + sm->pairwise_cipher); + return -1; + } + pos = wpabuf_put(buf, RSN_SELECTOR_LEN); + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + sm->pairwise_cipher)); + + /* Authenticated Key Management Suite Count */ + wpabuf_put_le16(buf, 1); + + /* Authenticated Key Management Suite List */ + pos = wpabuf_put(buf, RSN_SELECTOR_LEN); + if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256); + else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384); + else { + wpa_printf(MSG_WARNING, + "FILS+FT: Invalid key management type (%d)", + sm->key_mgmt); + return -1; + } + + /* RSN Capabilities */ + capab = 0; +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) + capab |= WPA_CAPABILITY_MFPC; +#endif /* CONFIG_IEEE80211W */ + wpabuf_put_le16(buf, capab); + + /* PMKID Count */ + wpabuf_put_le16(buf, 1); + + /* PMKID List [PMKR1Name] */ + wpa_hexdump_key(MSG_DEBUG, "FILS+FT: XXKey (FILS-FT)", + sm->fils_ft, sm->fils_ft_len); + wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: SSID", sm->ssid, sm->ssid_len); + wpa_hexdump(MSG_DEBUG, "FILS+FT: MDID", + sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN); + wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID", + sm->r0kh_id, sm->r0kh_id_len); + if (wpa_derive_pmk_r0(sm->fils_ft, sm->fils_ft_len, sm->ssid, + sm->ssid_len, sm->mobility_domain, + sm->r0kh_id, sm->r0kh_id_len, sm->own_addr, + sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0) { + wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMK-R0"); + return -1; + } + sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN; + wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0", + sm->pmk_r0, sm->pmk_r0_len); + wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name", + sm->pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_printf(MSG_DEBUG, "FILS+FT: R1KH-ID: " MACSTR, + MAC2STR(sm->r1kh_id)); + pos = wpabuf_put(buf, WPA_PMK_NAME_LEN); + if (wpa_derive_pmk_r1_name(sm->pmk_r0_name, sm->r1kh_id, sm->own_addr, + pos, use_sha384) < 0) { + wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", pos, WPA_PMK_NAME_LEN); + +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { + /* Management Group Cipher Suite */ + pos = wpabuf_put(buf, RSN_SELECTOR_LEN); + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + } +#endif /* CONFIG_IEEE80211W */ + + rsnie->len = ((u8 *) wpabuf_put(buf, 0) - (u8 *) rsnie) - 2; + return 0; +} +#endif /* CONFIG_IEEE80211R */ + + +struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek, + size_t *kek_len, const u8 **snonce, + const u8 **anonce, + const struct wpabuf **hlp, + unsigned int num_hlp) +{ + struct wpabuf *buf; + size_t len; + unsigned int i; + + len = 1000; +#ifdef CONFIG_IEEE80211R + if (sm->fils_ft_ies) + len += wpabuf_len(sm->fils_ft_ies); + if (wpa_key_mgmt_ft(sm->key_mgmt)) + len += 256; +#endif /* CONFIG_IEEE80211R */ + for (i = 0; hlp && i < num_hlp; i++) + len += 10 + wpabuf_len(hlp[i]); + buf = wpabuf_alloc(len); + if (!buf) + return NULL; + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->fils_ft_ies) { + /* MDE and FTE when using FILS+FT */ + wpabuf_put_buf(buf, sm->fils_ft_ies); + /* RSNE with PMKR1Name in PMKID field */ + if (fils_ft_build_assoc_req_rsne(sm, buf) < 0) { + wpabuf_free(buf); + return NULL; + } + } +#endif /* CONFIG_IEEE80211R */ + + /* FILS Session */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION); + wpabuf_put_data(buf, sm->fils_session, FILS_SESSION_LEN); + + /* Everything after FILS Session element gets encrypted in the driver + * with KEK. The buffer returned from here is the plaintext version. */ + + /* TODO: FILS Public Key */ + + /* FILS Key Confirm */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(buf, 1 + sm->fils_key_auth_len); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_KEY_CONFIRM); + wpabuf_put_data(buf, sm->fils_key_auth_sta, sm->fils_key_auth_len); + + /* FILS HLP Container */ + for (i = 0; hlp && i < num_hlp; i++) { + const u8 *pos = wpabuf_head(hlp[i]); + size_t left = wpabuf_len(hlp[i]); + + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ + if (left <= 254) + len = 1 + left; + else + len = 255; + wpabuf_put_u8(buf, len); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_HLP_CONTAINER); + /* Destination MAC Address, Source MAC Address, HLP Packet. + * HLP Packet is in MSDU format (i.e., included the LLC/SNAP + * header when LPD is used). */ + wpabuf_put_data(buf, pos, len - 1); + pos += len - 1; + left -= len - 1; + while (left) { + wpabuf_put_u8(buf, WLAN_EID_FRAGMENT); + len = left > 255 ? 255 : left; + wpabuf_put_u8(buf, len); + wpabuf_put_data(buf, pos, len); + pos += len; + left -= len; + } + } + + /* TODO: FILS IP Address Assignment */ + + wpa_hexdump_buf(MSG_DEBUG, "FILS: Association Request plaintext", buf); + + *kek = sm->ptk.kek; + *kek_len = sm->ptk.kek_len; + wpa_hexdump_key(MSG_DEBUG, "FILS: KEK for AEAD", *kek, *kek_len); + *snonce = sm->fils_nonce; + wpa_hexdump(MSG_DEBUG, "FILS: SNonce for AEAD AAD", + *snonce, FILS_NONCE_LEN); + *anonce = sm->fils_anonce; + wpa_hexdump(MSG_DEBUG, "FILS: ANonce for AEAD AAD", + *anonce, FILS_NONCE_LEN); + + return buf; +} + + +static void fils_process_hlp_resp(struct wpa_sm *sm, const u8 *resp, size_t len) +{ + const u8 *pos, *end; + + wpa_hexdump(MSG_MSGDUMP, "FILS: HLP response", resp, len); + if (len < 2 * ETH_ALEN) + return; + pos = resp + 2 * ETH_ALEN; + end = resp + len; + if (end - pos >= 6 && + os_memcmp(pos, "\xaa\xaa\x03\x00\x00\x00", 6) == 0) + pos += 6; /* Remove SNAP/LLC header */ + wpa_sm_fils_hlp_rx(sm, resp, resp + ETH_ALEN, pos, end - pos); +} + + +static void fils_process_hlp_container(struct wpa_sm *sm, const u8 *pos, + size_t len) +{ + const u8 *end = pos + len; + u8 *tmp, *tmp_pos; + + /* Check if there are any FILS HLP Container elements */ + while (end - pos >= 2) { + if (2 + pos[1] > end - pos) + return; + if (pos[0] == WLAN_EID_EXTENSION && + pos[1] >= 1 + 2 * ETH_ALEN && + pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER) + break; + pos += 2 + pos[1]; + } + if (end - pos < 2) + return; /* No FILS HLP Container elements */ + + tmp = os_malloc(end - pos); + if (!tmp) + return; + + while (end - pos >= 2) { + if (2 + pos[1] > end - pos || + pos[0] != WLAN_EID_EXTENSION || + pos[1] < 1 + 2 * ETH_ALEN || + pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER) + break; + tmp_pos = tmp; + os_memcpy(tmp_pos, pos + 3, pos[1] - 1); + tmp_pos += pos[1] - 1; + pos += 2 + pos[1]; + + /* Add possible fragments */ + while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT && + 2 + pos[1] <= end - pos) { + os_memcpy(tmp_pos, pos + 2, pos[1]); + tmp_pos += pos[1]; + pos += 2 + pos[1]; + } + + fils_process_hlp_resp(sm, tmp, tmp_pos - tmp); + } + + os_free(tmp); +} + + +int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + const u8 *end, *ie_start; + struct ieee802_11_elems elems; + int keylen, rsclen; + enum wpa_alg alg; + struct wpa_gtk_data gd; + int maxkeylen; + struct wpa_eapol_ie_parse kde; + + if (!sm || !sm->ptk_set) { + wpa_printf(MSG_DEBUG, "FILS: No KEK available"); + return -1; + } + + if (!wpa_key_mgmt_fils(sm->key_mgmt)) { + wpa_printf(MSG_DEBUG, "FILS: Not a FILS AKM"); + return -1; + } + + if (sm->fils_completed) { + wpa_printf(MSG_DEBUG, + "FILS: Association has already been completed for this FILS authentication - ignore unexpected retransmission"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "FILS: (Re)Association Response frame", + resp, len); + + mgmt = (const struct ieee80211_mgmt *) resp; + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_resp)) + return -1; + + end = resp + len; + /* Same offset for Association Response and Reassociation Response */ + ie_start = mgmt->u.assoc_resp.variable; + + if (ieee802_11_parse_elems(ie_start, end - ie_start, &elems, 1) == + ParseFailed) { + wpa_printf(MSG_DEBUG, + "FILS: Failed to parse decrypted elements"); + goto fail; + } + + if (!elems.fils_session) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Session element"); + return -1; + } + if (os_memcmp(elems.fils_session, sm->fils_session, + FILS_SESSION_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FILS: FILS Session mismatch"); + wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session", + elems.fils_session, FILS_SESSION_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session", + sm->fils_session, FILS_SESSION_LEN); + } + + /* TODO: FILS Public Key */ + + if (!elems.fils_key_confirm) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element"); + goto fail; + } + if (elems.fils_key_confirm_len != sm->fils_key_auth_len) { + wpa_printf(MSG_DEBUG, + "FILS: Unexpected Key-Auth length %d (expected %d)", + elems.fils_key_confirm_len, + (int) sm->fils_key_auth_len); + goto fail; + } + if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_ap, + sm->fils_key_auth_len) != 0) { + wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch"); + wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth", + elems.fils_key_confirm, + elems.fils_key_confirm_len); + wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth", + sm->fils_key_auth_ap, sm->fils_key_auth_len); + goto fail; + } + + /* Key Delivery */ + if (!elems.key_delivery) { + wpa_printf(MSG_DEBUG, "FILS: No Key Delivery element"); + goto fail; + } + + /* Parse GTK and set the key to the driver */ + os_memset(&gd, 0, sizeof(gd)); + if (wpa_supplicant_parse_ies(elems.key_delivery + WPA_KEY_RSC_LEN, + elems.key_delivery_len - WPA_KEY_RSC_LEN, + &kde) < 0) { + wpa_printf(MSG_DEBUG, "FILS: Failed to parse KDEs"); + goto fail; + } + if (!kde.gtk) { + wpa_printf(MSG_DEBUG, "FILS: No GTK KDE"); + goto fail; + } + maxkeylen = gd.gtk_len = kde.gtk_len - 2; + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, + gd.gtk_len, maxkeylen, + &gd.key_rsc_len, &gd.alg)) + goto fail; + + wpa_hexdump_key(MSG_DEBUG, "FILS: Received GTK", kde.gtk, kde.gtk_len); + gd.keyidx = kde.gtk[0] & 0x3; + gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm, + !!(kde.gtk[0] & BIT(2))); + if (kde.gtk_len - 2 > sizeof(gd.gtk)) { + wpa_printf(MSG_DEBUG, "FILS: Too long GTK in GTK KDE (len=%lu)", + (unsigned long) kde.gtk_len - 2); + goto fail; + } + os_memcpy(gd.gtk, kde.gtk + 2, kde.gtk_len - 2); + + wpa_printf(MSG_DEBUG, "FILS: Set GTK to driver"); + if (wpa_supplicant_install_gtk(sm, &gd, elems.key_delivery, 0) < 0) { + wpa_printf(MSG_DEBUG, "FILS: Failed to set GTK"); + goto fail; + } + + if (ieee80211w_set_keys(sm, &kde) < 0) { + wpa_printf(MSG_DEBUG, "FILS: Failed to set IGTK"); + goto fail; + } + + alg = wpa_cipher_to_alg(sm->pairwise_cipher); + keylen = wpa_cipher_key_len(sm->pairwise_cipher); + if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) { + wpa_printf(MSG_DEBUG, "FILS: TK length mismatch: %u != %lu", + keylen, (long unsigned int) sm->ptk.tk_len); + goto fail; + } + rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher); + wpa_hexdump_key(MSG_DEBUG, "FILS: Set TK to driver", + sm->ptk.tk, keylen); + if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, null_rsc, rsclen, + sm->ptk.tk, keylen) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "FILS: Failed to set PTK to the driver (alg=%d keylen=%d bssid=" + MACSTR ")", + alg, keylen, MAC2STR(sm->bssid)); + goto fail; + } + + /* TODO: TK could be cleared after auth frame exchange now that driver + * takes care of association frame encryption/decryption. */ + /* TK is not needed anymore in supplicant */ + os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN); + sm->ptk.tk_len = 0; + sm->ptk.installed = 1; + + /* FILS HLP Container */ + fils_process_hlp_container(sm, ie_start, end - ie_start); + + /* TODO: FILS IP Address Assignment */ + + wpa_printf(MSG_DEBUG, "FILS: Auth+Assoc completed successfully"); + sm->fils_completed = 1; + + return 0; +fail: + return -1; +} + + +void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set) +{ + if (sm) + sm->fils_completed = !!set; +} + +#endif /* CONFIG_FILS */ + + +int wpa_fils_is_completed(struct wpa_sm *sm) +{ +#ifdef CONFIG_FILS + return sm && sm->fils_completed; +#else /* CONFIG_FILS */ + return 0; +#endif /* CONFIG_FILS */ +} + + +#ifdef CONFIG_OWE + +struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group) +{ + struct wpabuf *ie = NULL, *pub = NULL; + size_t prime_len; + + if (group == 19) + prime_len = 32; + else if (group == 20) + prime_len = 48; + else if (group == 21) + prime_len = 66; + else + return NULL; + + crypto_ecdh_deinit(sm->owe_ecdh); + sm->owe_ecdh = crypto_ecdh_init(group); + if (!sm->owe_ecdh) + goto fail; + sm->owe_group = group; + pub = crypto_ecdh_get_pubkey(sm->owe_ecdh, 0); + pub = wpabuf_zeropad(pub, prime_len); + if (!pub) + goto fail; + + ie = wpabuf_alloc(5 + wpabuf_len(pub)); + if (!ie) + goto fail; + wpabuf_put_u8(ie, WLAN_EID_EXTENSION); + wpabuf_put_u8(ie, 1 + 2 + wpabuf_len(pub)); + wpabuf_put_u8(ie, WLAN_EID_EXT_OWE_DH_PARAM); + wpabuf_put_le16(ie, group); + wpabuf_put_buf(ie, pub); + wpabuf_free(pub); + wpa_hexdump_buf(MSG_DEBUG, "OWE: Diffie-Hellman Parameter element", + ie); + + return ie; +fail: + wpabuf_free(pub); + crypto_ecdh_deinit(sm->owe_ecdh); + sm->owe_ecdh = NULL; + return NULL; +} + + +int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid, + const u8 *resp_ies, size_t resp_ies_len) +{ + struct ieee802_11_elems elems; + u16 group; + struct wpabuf *secret, *pub, *hkey; + int res; + u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN]; + const char *info = "OWE Key Generation"; + const u8 *addr[2]; + size_t len[2]; + size_t hash_len, prime_len; + struct wpa_ie_data data; + + if (!resp_ies || + ieee802_11_parse_elems(resp_ies, resp_ies_len, &elems, 1) == + ParseFailed) { + wpa_printf(MSG_INFO, + "OWE: Could not parse Association Response frame elements"); + return -1; + } + + if (sm->cur_pmksa && elems.rsn_ie && + wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, 2 + elems.rsn_ie_len, + &data) == 0 && + data.num_pmkid == 1 && data.pmkid && + os_memcmp(sm->cur_pmksa->pmkid, data.pmkid, PMKID_LEN) == 0) { + wpa_printf(MSG_DEBUG, "OWE: Use PMKSA caching"); + wpa_sm_set_pmk_from_pmksa(sm); + return 0; + } + + if (!elems.owe_dh) { + wpa_printf(MSG_INFO, + "OWE: No Diffie-Hellman Parameter element found in Association Response frame"); + return -1; + } + + group = WPA_GET_LE16(elems.owe_dh); + if (group != sm->owe_group) { + wpa_printf(MSG_INFO, + "OWE: Unexpected Diffie-Hellman group in response: %u", + group); + return -1; + } + + if (!sm->owe_ecdh) { + wpa_printf(MSG_INFO, "OWE: No ECDH state available"); + return -1; + } + + if (group == 19) + prime_len = 32; + else if (group == 20) + prime_len = 48; + else if (group == 21) + prime_len = 66; + else + return -1; + + secret = crypto_ecdh_set_peerkey(sm->owe_ecdh, 0, + elems.owe_dh + 2, + elems.owe_dh_len - 2); + secret = wpabuf_zeropad(secret, prime_len); + if (!secret) { + wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key"); + return -1; + } + wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret); + + /* prk = HKDF-extract(C | A | group, z) */ + + pub = crypto_ecdh_get_pubkey(sm->owe_ecdh, 0); + if (!pub) { + wpabuf_clear_free(secret); + return -1; + } + + /* PMKID = Truncate-128(Hash(C | A)) */ + addr[0] = wpabuf_head(pub); + len[0] = wpabuf_len(pub); + addr[1] = elems.owe_dh + 2; + len[1] = elems.owe_dh_len - 2; + if (group == 19) { + res = sha256_vector(2, addr, len, pmkid); + hash_len = SHA256_MAC_LEN; + } else if (group == 20) { + res = sha384_vector(2, addr, len, pmkid); + hash_len = SHA384_MAC_LEN; + } else if (group == 21) { + res = sha512_vector(2, addr, len, pmkid); + hash_len = SHA512_MAC_LEN; + } else { + res = -1; + hash_len = 0; + } + pub = wpabuf_zeropad(pub, prime_len); + if (res < 0 || !pub) { + wpabuf_free(pub); + wpabuf_clear_free(secret); + return -1; + } + + hkey = wpabuf_alloc(wpabuf_len(pub) + elems.owe_dh_len - 2 + 2); + if (!hkey) { + wpabuf_free(pub); + wpabuf_clear_free(secret); + return -1; + } + + wpabuf_put_buf(hkey, pub); /* C */ + wpabuf_free(pub); + wpabuf_put_data(hkey, elems.owe_dh + 2, elems.owe_dh_len - 2); /* A */ + wpabuf_put_le16(hkey, sm->owe_group); /* group */ + if (group == 19) + res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey), + wpabuf_head(secret), wpabuf_len(secret), prk); + else if (group == 20) + res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey), + wpabuf_head(secret), wpabuf_len(secret), prk); + else if (group == 21) + res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey), + wpabuf_head(secret), wpabuf_len(secret), prk); + wpabuf_clear_free(hkey); + wpabuf_clear_free(secret); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len); + + /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */ + + if (group == 19) + res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info, + os_strlen(info), sm->pmk, hash_len); + else if (group == 20) + res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info, + os_strlen(info), sm->pmk, hash_len); + else if (group == 21) + res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info, + os_strlen(info), sm->pmk, hash_len); + os_memset(prk, 0, SHA512_MAC_LEN); + if (res < 0) { + sm->pmk_len = 0; + return -1; + } + sm->pmk_len = hash_len; + + wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sm->pmk, sm->pmk_len); + wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN); + pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, pmkid, NULL, 0, + bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt, + NULL); + + return 0; +} + +#endif /* CONFIG_OWE */ + + +void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id) +{ +#ifdef CONFIG_FILS + if (sm && fils_cache_id) { + sm->fils_cache_id_set = 1; + os_memcpy(sm->fils_cache_id, fils_cache_id, FILS_CACHE_ID_LEN); + } +#endif /* CONFIG_FILS */ +} diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 0b7477f31bc7..21f4b17815e9 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -25,7 +25,7 @@ struct wpa_sm_ctx { void (*set_state)(void *ctx, enum wpa_states state); enum wpa_states (*get_state)(void *ctx); - void (*deauthenticate)(void * ctx, int reason_code); + void (*deauthenticate)(void * ctx, int reason_code); int (*set_key)(void *ctx, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, @@ -38,8 +38,11 @@ struct wpa_sm_ctx { void (*cancel_auth_timeout)(void *ctx); u8 * (*alloc_eapol)(void *ctx, u8 type, const void *data, u16 data_len, size_t *msg_len, void **data_pos); - int (*add_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid); - int (*remove_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid); + int (*add_pmkid)(void *ctx, void *network_ctx, const u8 *bssid, + const u8 *pmkid, const u8 *fils_cache_id, + const u8 *pmk, size_t pmk_len); + int (*remove_pmkid)(void *ctx, void *network_ctx, const u8 *bssid, + const u8 *pmkid, const u8 *fils_cache_id); void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob); const struct wpa_config_blob * (*get_config_blob)(void *ctx, const char *name); @@ -77,6 +80,8 @@ struct wpa_sm_ctx { const u8 *kck, size_t kck_len, const u8 *replay_ctr); int (*key_mgmt_set_pmk)(void *ctx, const u8 *pmk, size_t pmk_len); + void (*fils_hlp_rx)(void *ctx, const u8 *dst, const u8 *src, + const u8 *pkt, size_t pkt_len); }; @@ -95,7 +100,6 @@ enum wpa_sm_conf_params { struct rsn_supp_config { void *network_ctx; - int peerkey_enabled; int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */ int proactive_key_caching; int eap_workaround; @@ -105,6 +109,7 @@ struct rsn_supp_config { int wpa_ptk_rekey; int p2p; int wpa_rsc_relaxation; + const u8 *fils_cache_id; }; #ifndef CONFIG_NO_WPA @@ -147,6 +152,15 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len); int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data); int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len); +struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_head(struct wpa_sm *sm); +struct rsn_pmksa_cache_entry * +wpa_sm_pmksa_cache_add_entry(struct wpa_sm *sm, + struct rsn_pmksa_cache_entry * entry); +void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, + const u8 *pmkid, const u8 *bssid, + const u8 *fils_cache_id); +int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid, + const void *network_ctx); void wpa_sm_drop_sa(struct wpa_sm *sm); int wpa_sm_has_ptk(struct wpa_sm *sm); @@ -160,6 +174,7 @@ void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter); void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck, size_t ptk_kck_len, const u8 *ptk_kek, size_t ptk_kek_len); +int wpa_fils_is_completed(struct wpa_sm *sm); #else /* CONFIG_NO_WPA */ @@ -327,29 +342,20 @@ static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck, { } -#endif /* CONFIG_NO_WPA */ - -#ifdef CONFIG_PEERKEY -int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer); -int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, - const u8 *buf, size_t len); -#else /* CONFIG_PEERKEY */ -static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) -{ - return -1; -} - -static inline int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, - const u8 *buf, size_t len) +static inline int wpa_fils_is_completed(struct wpa_sm *sm) { return 0; } -#endif /* CONFIG_PEERKEY */ + +#endif /* CONFIG_NO_WPA */ #ifdef CONFIG_IEEE80211R int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len); int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie); +int wpa_ft_add_mdie(struct wpa_sm *sm, u8 *ies, size_t ies_len, + const u8 *mdie); +const u8 * wpa_sm_get_ft_md(struct wpa_sm *sm); int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, int ft_action, const u8 *target_ap, const u8 *ric_ies, size_t ric_ies_len); @@ -374,6 +380,12 @@ static inline int wpa_ft_prepare_auth_request(struct wpa_sm *sm, return 0; } +static inline int wpa_ft_add_mdie(struct wpa_sm *sm, u8 *ies, size_t ies_len, + const u8 *mdie) +{ + return 0; +} + static inline int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, int ft_action, const u8 *target_ap) @@ -425,5 +437,24 @@ extern unsigned int tdls_testing; int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf); void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf); +const u8 * wpa_sm_get_anonce(struct wpa_sm *sm); +unsigned int wpa_sm_get_key_mgmt(struct wpa_sm *sm); + +struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md); +int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data, + size_t len); +struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek, + size_t *kek_len, const u8 **snonce, + const u8 **anonce, + const struct wpabuf **hlp, + unsigned int num_hlp); +int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len); + +struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group); +int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid, + const u8 *resp_ies, size_t resp_ies_len); + +void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set); +void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id); #endif /* WPA_H */ diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index d45bb4585e50..b8d60e3208d0 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - IEEE 802.11r - Fast BSS Transition - * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2018, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,7 @@ #include "common.h" #include "crypto/aes_wrap.h" +#include "crypto/sha384.h" #include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" @@ -23,6 +24,7 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, { u8 ptk_name[WPA_PMK_NAME_LEN]; const u8 *anonce = key->key_nonce; + int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt); if (sm->xxkey_len == 0) { wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " @@ -30,21 +32,26 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, return -1; } - wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid, - sm->ssid_len, sm->mobility_domain, - sm->r0kh_id, sm->r0kh_id_len, sm->own_addr, - sm->pmk_r0, sm->pmk_r0_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, PMK_LEN); + sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN; + if (wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid, + sm->ssid_len, sm->mobility_domain, + sm->r0kh_id, sm->r0kh_id_len, sm->own_addr, + sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, sm->pmk_r0_len); wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", sm->pmk_r0_name, WPA_PMK_NAME_LEN); - wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id, - sm->own_addr, sm->pmk_r1, sm->pmk_r1_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN); + sm->pmk_r1_len = sm->pmk_r0_len; + if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name, + sm->r1kh_id, sm->own_addr, sm->pmk_r1, + sm->pmk_r1_name) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, sm->pmk_r1_len); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); - return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr, - sm->bssid, sm->pmk_r1_name, ptk, ptk_name, - sm->key_mgmt, sm->pairwise_cipher); + return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce, anonce, + sm->own_addr, sm->bssid, sm->pmk_r1_name, ptk, + ptk_name, sm->key_mgmt, sm->pairwise_cipher); } @@ -58,11 +65,13 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) { struct wpa_ft_ies ft; + int use_sha384; if (sm == NULL) return 0; - if (wpa_ft_parse_ies(ies, ies_len, &ft) < 0) + use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt); + if (wpa_ft_parse_ies(ies, ies_len, &ft, use_sha384) < 0) return -1; if (ft.mdie && ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) @@ -146,16 +155,17 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, const u8 *ap_mdie) { size_t buf_len; - u8 *buf, *pos, *ftie_len, *ftie_pos; + u8 *buf, *pos, *ftie_len, *ftie_pos, *fte_mic, *elem_count; struct rsn_mdie *mdie; - struct rsn_ftie *ftie; struct rsn_ie_hdr *rsnie; u16 capab; + int mdie_len; sm->ft_completed = 0; sm->ft_reassoc_completed = 0; - buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + + buf_len = 2 + sizeof(struct rsn_mdie) + 2 + + sizeof(struct rsn_ftie_sha384) + 2 + sm->r0kh_id_len + ric_ies_len + 100; buf = os_zalloc(buf_len); if (buf == NULL) @@ -201,10 +211,20 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, /* Authenticated Key Management Suite List */ if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); +#ifdef CONFIG_SHA384 + else if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X_SHA384) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384); +#endif /* CONFIG_SHA384 */ else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK) RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE) RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); +#ifdef CONFIG_FILS + else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256); + else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384); +#endif /* CONFIG_FILS */ else { wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)", sm->key_mgmt); @@ -216,7 +236,10 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, /* RSN Capabilities */ capab = 0; #ifdef CONFIG_IEEE80211W - if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) + if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC || + sm->mgmt_group_cipher == WPA_CIPHER_BIP_GMAC_128 || + sm->mgmt_group_cipher == WPA_CIPHER_BIP_GMAC_256 || + sm->mgmt_group_cipher == WPA_CIPHER_BIP_CMAC_256) capab |= WPA_CAPABILITY_MFPC; #endif /* CONFIG_IEEE80211W */ WPA_PUT_LE16(pos, capab); @@ -231,34 +254,63 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, pos += WPA_PMK_NAME_LEN; #ifdef CONFIG_IEEE80211W - if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { - /* Management Group Cipher Suite */ + /* Management Group Cipher Suite */ + switch (sm->mgmt_group_cipher) { + case WPA_CIPHER_AES_128_CMAC: RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); pos += RSN_SELECTOR_LEN; + break; + case WPA_CIPHER_BIP_GMAC_128: + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_128); + pos += RSN_SELECTOR_LEN; + break; + case WPA_CIPHER_BIP_GMAC_256: + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_256); + pos += RSN_SELECTOR_LEN; + break; + case WPA_CIPHER_BIP_CMAC_256: + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_CMAC_256); + pos += RSN_SELECTOR_LEN; + break; } #endif /* CONFIG_IEEE80211W */ rsnie->len = (pos - (u8 *) rsnie) - 2; /* MDIE */ - *pos++ = WLAN_EID_MOBILITY_DOMAIN; - *pos++ = sizeof(*mdie); - mdie = (struct rsn_mdie *) pos; - pos += sizeof(*mdie); - os_memcpy(mdie->mobility_domain, sm->mobility_domain, - MOBILITY_DOMAIN_ID_LEN); - mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] : - sm->mdie_ft_capab; + mdie_len = wpa_ft_add_mdie(sm, pos, buf_len - (pos - buf), ap_mdie); + if (mdie_len <= 0) { + os_free(buf); + return NULL; + } + mdie = (struct rsn_mdie *) (pos + 2); + pos += mdie_len; /* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */ ftie_pos = pos; *pos++ = WLAN_EID_FAST_BSS_TRANSITION; ftie_len = pos++; - ftie = (struct rsn_ftie *) pos; - pos += sizeof(*ftie); - os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN); - if (anonce) - os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN); + if (wpa_key_mgmt_sha384(sm->key_mgmt)) { + struct rsn_ftie_sha384 *ftie; + + ftie = (struct rsn_ftie_sha384 *) pos; + fte_mic = ftie->mic; + elem_count = &ftie->mic_control[1]; + pos += sizeof(*ftie); + os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN); + if (anonce) + os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN); + } else { + struct rsn_ftie *ftie; + + ftie = (struct rsn_ftie *) pos; + fte_mic = ftie->mic; + elem_count = &ftie->mic_control[1]; + pos += sizeof(*ftie); + os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN); + if (anonce) + os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN); + } if (kck) { /* R1KH-ID sub-element in third FT message */ *pos++ = FTIE_SUBELEM_R1KH_ID; @@ -292,13 +344,12 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, * RIC-Request (if present) */ /* Information element count */ - ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies, - ric_ies_len); + *elem_count = 3 + ieee802_11_ie_count(ric_ies, ric_ies_len); if (wpa_ft_mic(kck, kck_len, sm->own_addr, target_ap, 5, ((u8 *) mdie) - 2, 2 + sizeof(*mdie), ftie_pos, 2 + *ftie_len, (u8 *) rsnie, 2 + rsnie->len, ric_ies, - ric_ies_len, ftie->mic) < 0) { + ric_ies_len, fte_mic) < 0) { wpa_printf(MSG_INFO, "FT: Failed to calculate MIC"); os_free(buf); return NULL; @@ -367,6 +418,37 @@ int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie) } +int wpa_ft_add_mdie(struct wpa_sm *sm, u8 *buf, size_t buf_len, + const u8 *ap_mdie) +{ + u8 *pos = buf; + struct rsn_mdie *mdie; + + if (buf_len < 2 + sizeof(*mdie)) { + wpa_printf(MSG_INFO, + "FT: Failed to add MDIE: short buffer, length=%zu", + buf_len); + return 0; + } + + *pos++ = WLAN_EID_MOBILITY_DOMAIN; + *pos++ = sizeof(*mdie); + mdie = (struct rsn_mdie *) pos; + os_memcpy(mdie->mobility_domain, sm->mobility_domain, + MOBILITY_DOMAIN_ID_LEN); + mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] : + sm->mdie_ft_capab; + + return 2 + sizeof(*mdie); +} + + +const u8 * wpa_sm_get_ft_md(struct wpa_sm *sm) +{ + return sm->mobility_domain; +} + + int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, int ft_action, const u8 *target_ap, const u8 *ric_ies, size_t ric_ies_len) @@ -375,10 +457,13 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, size_t ft_ies_len; struct wpa_ft_ies parse; struct rsn_mdie *mdie; - struct rsn_ftie *ftie; u8 ptk_name[WPA_PMK_NAME_LEN]; int ret; const u8 *bssid; + const u8 *kck; + size_t kck_len; + int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt); + const u8 *anonce, *snonce; wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len); @@ -404,7 +489,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, return -1; } - if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); return -1; } @@ -417,16 +502,34 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, return -1; } - ftie = (struct rsn_ftie *) parse.ftie; - if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { - wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); - return -1; + if (use_sha384) { + struct rsn_ftie_sha384 *ftie; + + ftie = (struct rsn_ftie_sha384 *) parse.ftie; + if (!ftie || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return -1; + } + + anonce = ftie->anonce; + snonce = ftie->snonce; + } else { + struct rsn_ftie *ftie; + + ftie = (struct rsn_ftie *) parse.ftie; + if (!ftie || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return -1; + } + + anonce = ftie->anonce; + snonce = ftie->snonce; } - if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) { + if (os_memcmp(snonce, sm->snonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", - ftie->snonce, WPA_NONCE_LEN); + snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", sm->snonce, WPA_NONCE_LEN); return -1; @@ -465,23 +568,34 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN); - wpa_hexdump(MSG_DEBUG, "FT: ANonce", ftie->anonce, WPA_NONCE_LEN); - os_memcpy(sm->anonce, ftie->anonce, WPA_NONCE_LEN); - wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id, - sm->own_addr, sm->pmk_r1, sm->pmk_r1_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: ANonce", anonce, WPA_NONCE_LEN); + os_memcpy(sm->anonce, anonce, WPA_NONCE_LEN); + if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name, + sm->r1kh_id, sm->own_addr, sm->pmk_r1, + sm->pmk_r1_name) < 0) + return -1; + sm->pmk_r1_len = sm->pmk_r0_len; + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, sm->pmk_r1_len); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); bssid = target_ap; - if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, - sm->own_addr, bssid, sm->pmk_r1_name, &sm->ptk, - ptk_name, sm->key_mgmt, sm->pairwise_cipher) < 0) + if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce, + anonce, sm->own_addr, bssid, + sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt, + sm->pairwise_cipher) < 0) return -1; - ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce, + if (wpa_key_mgmt_fils(sm->key_mgmt)) { + kck = sm->ptk.kck2; + kck_len = sm->ptk.kck2_len; + } else { + kck = sm->ptk.kck; + kck_len = sm->ptk.kck_len; + } + ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, anonce, sm->pmk_r1_name, - sm->ptk.kck, sm->ptk.kck_len, bssid, + kck, kck_len, bssid, ric_ies, ric_ies_len, parse.mdie ? parse.mdie - 2 : NULL); if (ft_ies) { @@ -544,6 +658,16 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, int keyidx; enum wpa_alg alg; size_t gtk_len, keylen, rsc_len; + const u8 *kek; + size_t kek_len; + + if (wpa_key_mgmt_fils(sm->key_mgmt)) { + kek = sm->ptk.kek2; + kek_len = sm->ptk.kek2_len; + } else { + kek = sm->ptk.kek; + kek_len = sm->ptk.kek_len; + } if (gtk_elem == NULL) { wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE"); @@ -560,8 +684,7 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, return -1; } gtk_len = gtk_elem_len - 19; - if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, gtk_len / 8, gtk_elem + 11, - gtk)) { + if (aes_unwrap(kek, kek_len, gtk_len / 8, gtk_elem + 11, gtk)) { wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " "decrypt GTK"); return -1; @@ -615,10 +738,24 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, size_t igtk_elem_len) { - u8 igtk[WPA_IGTK_LEN]; + u8 igtk[WPA_IGTK_MAX_LEN]; + size_t igtk_len; u16 keyidx; + const u8 *kek; + size_t kek_len; + + if (wpa_key_mgmt_fils(sm->key_mgmt)) { + kek = sm->ptk.kek2; + kek_len = sm->ptk.kek2_len; + } else { + kek = sm->ptk.kek; + kek_len = sm->ptk.kek_len; + } - if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) + if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC && + sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_128 && + sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_256 && + sm->mgmt_group_cipher != WPA_CIPHER_BIP_CMAC_256) return 0; if (igtk_elem == NULL) { @@ -629,19 +766,19 @@ static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, wpa_hexdump_key(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp", igtk_elem, igtk_elem_len); - if (igtk_elem_len != 2 + 6 + 1 + WPA_IGTK_LEN + 8) { + igtk_len = wpa_cipher_key_len(sm->mgmt_group_cipher); + if (igtk_elem_len != 2 + 6 + 1 + igtk_len + 8) { wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem " "length %lu", (unsigned long) igtk_elem_len); return -1; } - if (igtk_elem[8] != WPA_IGTK_LEN) { + if (igtk_elem[8] != igtk_len) { wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem Key Length " "%d", igtk_elem[8]); return -1; } - if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, WPA_IGTK_LEN / 8, - igtk_elem + 9, igtk)) { + if (aes_unwrap(kek, kek_len, igtk_len / 8, igtk_elem + 9, igtk)) { wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " "decrypt IGTK"); return -1; @@ -652,13 +789,16 @@ static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, keyidx = WPA_GET_LE16(igtk_elem); wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk, - WPA_IGTK_LEN); - if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0, - igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < 0) { + igtk_len); + if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher), + broadcast_ether_addr, keyidx, 0, + igtk_elem + 2, 6, igtk, igtk_len) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the " "driver."); + os_memset(igtk, 0, sizeof(igtk)); return -1; } + os_memset(igtk, 0, sizeof(igtk)); return 0; } @@ -670,9 +810,13 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, { struct wpa_ft_ies parse; struct rsn_mdie *mdie; - struct rsn_ftie *ftie; unsigned int count; u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; + const u8 *kck; + size_t kck_len; + int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt); + const u8 *anonce, *snonce, *fte_mic; + u8 fte_elem_count; wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); @@ -687,7 +831,7 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return 0; } - if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); return -1; } @@ -700,25 +844,47 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return -1; } - ftie = (struct rsn_ftie *) parse.ftie; - if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { - wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); - return -1; + if (use_sha384) { + struct rsn_ftie_sha384 *ftie; + + ftie = (struct rsn_ftie_sha384 *) parse.ftie; + if (!ftie || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return -1; + } + + anonce = ftie->anonce; + snonce = ftie->snonce; + fte_elem_count = ftie->mic_control[1]; + fte_mic = ftie->mic; + } else { + struct rsn_ftie *ftie; + + ftie = (struct rsn_ftie *) parse.ftie; + if (!ftie || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return -1; + } + + anonce = ftie->anonce; + snonce = ftie->snonce; + fte_elem_count = ftie->mic_control[1]; + fte_mic = ftie->mic; } - if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) { + if (os_memcmp(snonce, sm->snonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", - ftie->snonce, WPA_NONCE_LEN); + snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", sm->snonce, WPA_NONCE_LEN); return -1; } - if (os_memcmp(ftie->anonce, sm->anonce, WPA_NONCE_LEN) != 0) { + if (os_memcmp(anonce, sm->anonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received ANonce", - ftie->anonce, WPA_NONCE_LEN); + anonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", sm->anonce, WPA_NONCE_LEN); return -1; @@ -763,14 +929,22 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, count = 3; if (parse.ric) count += ieee802_11_ie_count(parse.ric, parse.ric_len); - if (ftie->mic_control[1] != count) { + if (fte_elem_count != count) { wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " "Control: received %u expected %u", - ftie->mic_control[1], count); + fte_elem_count, count); return -1; } - if (wpa_ft_mic(sm->ptk.kck, sm->ptk.kck_len, sm->own_addr, src_addr, 6, + if (wpa_key_mgmt_fils(sm->key_mgmt)) { + kck = sm->ptk.kck2; + kck_len = sm->ptk.kck2_len; + } else { + kck = sm->ptk.kck; + kck_len = sm->ptk.kck_len; + } + + if (wpa_ft_mic(kck, kck_len, sm->own_addr, src_addr, 6, parse.mdie - 2, parse.mdie_len + 2, parse.ftie - 2, parse.ftie_len + 2, parse.rsn - 2, parse.rsn_len + 2, @@ -780,9 +954,9 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return -1; } - if (os_memcmp_const(mic, ftie->mic, 16) != 0) { + if (os_memcmp_const(mic, fte_mic, 16) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); - wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16); + wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", fte_mic, 16); wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); return -1; } diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 56f88dcdd899..b94b17a85a3a 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -1,6 +1,6 @@ /* * Internal WPA/RSN supplicant state machine definitions - * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,7 +11,6 @@ #include "utils/list.h" -struct wpa_peerkey; struct wpa_tdls_peer; struct wpa_eapol_key; @@ -57,7 +56,6 @@ struct wpa_sm { int fast_reauth; /* whether EAP fast re-authentication is enabled */ void *network_ctx; - int peerkey_enabled; int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */ int proactive_key_caching; int eap_workaround; @@ -94,9 +92,6 @@ struct wpa_sm { u8 *ap_wpa_ie, *ap_rsn_ie; size_t ap_wpa_ie_len, ap_rsn_ie_len; -#ifdef CONFIG_PEERKEY - struct wpa_peerkey *peerkey; -#endif /* CONFIG_PEERKEY */ #ifdef CONFIG_TDLS struct wpa_tdls_peer *tdls; int tdls_prohibited; @@ -117,11 +112,14 @@ struct wpa_sm { #endif /* CONFIG_TDLS */ #ifdef CONFIG_IEEE80211R - u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ + u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the + * first 384 bits of MSK */ size_t xxkey_len; - u8 pmk_r0[PMK_LEN]; + u8 pmk_r0[PMK_LEN_MAX]; + size_t pmk_r0_len; u8 pmk_r0_name[WPA_PMK_NAME_LEN]; - u8 pmk_r1[PMK_LEN]; + u8 pmk_r1[PMK_LEN_MAX]; + size_t pmk_r1_len; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; @@ -144,6 +142,31 @@ struct wpa_sm { #ifdef CONFIG_TESTING_OPTIONS struct wpabuf *test_assoc_ie; #endif /* CONFIG_TESTING_OPTIONS */ + +#ifdef CONFIG_FILS + u8 fils_nonce[FILS_NONCE_LEN]; + u8 fils_session[FILS_SESSION_LEN]; + u8 fils_anonce[FILS_NONCE_LEN]; + u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN]; + u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN]; + size_t fils_key_auth_len; + unsigned int fils_completed:1; + unsigned int fils_erp_pmkid_set:1; + unsigned int fils_cache_id_set:1; + u8 fils_erp_pmkid[PMKID_LEN]; + u8 fils_cache_id[FILS_CACHE_ID_LEN]; + struct crypto_ecdh *fils_ecdh; + int fils_dh_group; + size_t fils_dh_elem_len; + struct wpabuf *fils_ft_ies; + u8 fils_ft[FILS_FT_MAX_LEN]; + size_t fils_ft_len; +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + struct crypto_ecdh *owe_ecdh; + u16 owe_group; +#endif /* CONFIG_OWE */ }; @@ -215,18 +238,23 @@ static inline u8 * wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type, msg_len, data_pos); } -static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, const u8 *bssid, - const u8 *pmkid) +static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, void *network_ctx, + const u8 *bssid, const u8 *pmkid, + const u8 *cache_id, const u8 *pmk, + size_t pmk_len) { WPA_ASSERT(sm->ctx->add_pmkid); - return sm->ctx->add_pmkid(sm->ctx->ctx, bssid, pmkid); + return sm->ctx->add_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid, + cache_id, pmk, pmk_len); } -static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, const u8 *bssid, - const u8 *pmkid) +static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, void *network_ctx, + const u8 *bssid, const u8 *pmkid, + const u8 *cache_id) { WPA_ASSERT(sm->ctx->remove_pmkid); - return sm->ctx->remove_pmkid(sm->ctx->ctx, bssid, pmkid); + return sm->ctx->remove_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid, + cache_id); } static inline int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr, @@ -359,7 +387,16 @@ static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm, return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len); } -int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, +static inline void wpa_sm_fils_hlp_rx(struct wpa_sm *sm, + const u8 *dst, const u8 *src, + const u8 *pkt, size_t pkt_len) +{ + if (sm->ctx->fils_hlp_rx) + sm->ctx->fils_hlp_rx(sm->ctx->ctx, dst, src, pkt, pkt_len); +} + + +int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk, int ver, const u8 *dest, u16 proto, u8 *msg, size_t msg_len, u8 *key_mic); int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c index c44844ec583b..a3410d15447a 100644 --- a/src/rsn_supp/wpa_ie.c +++ b/src/rsn_supp/wpa_ie.c @@ -1,6 +1,6 @@ /* * wpa_supplicant - WPA/RSN IE and KDE processing - * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -161,6 +161,10 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, #ifdef CONFIG_IEEE80211R } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); +#ifdef CONFIG_SHA384 + } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384); +#endif /* CONFIG_SHA384 */ } else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); #endif /* CONFIG_IEEE80211R */ @@ -180,6 +184,30 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192); } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B); +#ifdef CONFIG_FILS + } else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256); + } else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384); +#ifdef CONFIG_IEEE80211R + } else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256); + } else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384); +#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_FILS */ +#ifdef CONFIG_OWE + } else if (key_mgmt & WPA_KEY_MGMT_OWE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE); +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + } else if (key_mgmt & WPA_KEY_MGMT_DPP) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP); +#endif /* CONFIG_DPP */ +#ifdef CONFIG_HS20 + } else if (key_mgmt & WPA_KEY_MGMT_OSEN) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN); +#endif /* CONFIG_HS20 */ } else { wpa_printf(MSG_WARNING, "Invalid key management type (%d).", key_mgmt); @@ -405,44 +433,6 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, return 0; } -#ifdef CONFIG_PEERKEY - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) { - ie->smk = pos + 2 + RSN_SELECTOR_LEN; - ie->smk_len = pos[1] - RSN_SELECTOR_LEN; - wpa_hexdump_key(MSG_DEBUG, "WPA: SMK in EAPOL-Key", - pos, pos[1] + 2); - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) { - ie->nonce = pos + 2 + RSN_SELECTOR_LEN; - ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; - wpa_hexdump(MSG_DEBUG, "WPA: Nonce in EAPOL-Key", - pos, pos[1] + 2); - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) { - ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; - ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; - wpa_hexdump(MSG_DEBUG, "WPA: Lifetime in EAPOL-Key", - pos, pos[1] + 2); - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) { - ie->error = pos + 2 + RSN_SELECTOR_LEN; - ie->error_len = pos[1] - RSN_SELECTOR_LEN; - wpa_hexdump(MSG_DEBUG, "WPA: Error in EAPOL-Key", - pos, pos[1] + 2); - return 0; - } -#endif /* CONFIG_PEERKEY */ - #ifdef CONFIG_IEEE80211W if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h index fe95af0abc51..0e72af56029e 100644 --- a/src/rsn_supp/wpa_ie.h +++ b/src/rsn_supp/wpa_ie.h @@ -21,16 +21,6 @@ struct wpa_eapol_ie_parse { size_t gtk_len; const u8 *mac_addr; size_t mac_addr_len; -#ifdef CONFIG_PEERKEY - const u8 *smk; - size_t smk_len; - const u8 *nonce; - size_t nonce_len; - const u8 *lifetime; - size_t lifetime_len; - const u8 *error; - size_t error_len; -#endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211W const u8 *igtk; size_t igtk_len; diff --git a/src/tls/libtommath.c b/src/tls/libtommath.c index 8bc824f20dcd..4f7a14823d72 100644 --- a/src/tls/libtommath.c +++ b/src/tls/libtommath.c @@ -116,7 +116,7 @@ typedef int mp_err; #define MP_PREC 32 /* default digits of precision */ #else #define MP_PREC 8 /* default digits of precision */ - #endif + #endif #endif /* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ @@ -274,8 +274,8 @@ static int s_mp_add (mp_int * a, mp_int * b, mp_int * c) *tmpc++ &= MP_MASK; } - /* now copy higher words if any, that is in A+B - * if A or B has more digits add those in + /* now copy higher words if any, that is in A+B + * if A or B has more digits add those in */ if (min != max) { for (; i < max; i++) { @@ -499,29 +499,29 @@ static int mp_mul (mp_int * a, mp_int * b, mp_int * c) #ifdef BN_MP_TOOM_MUL_C if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) { res = mp_toom_mul(a, b, c); - } else + } else #endif #ifdef BN_MP_KARATSUBA_MUL_C /* use Karatsuba? */ if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { res = mp_karatsuba_mul (a, b, c); - } else + } else #endif { /* can we use the fast multiplier? * - * The fast multiplier can be used if the output will - * have less than MP_WARRAY digits and the number of + * The fast multiplier can be used if the output will + * have less than MP_WARRAY digits and the number of * digits won't affect carry propagation */ #ifdef BN_FAST_S_MP_MUL_DIGS_C int digs = a->used + b->used + 1; if ((digs < MP_WARRAY) && - MIN(a->used, b->used) <= + MIN(a->used, b->used) <= (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { res = fast_s_mp_mul_digs (a, b, c, digs); - } else + } else #endif #ifdef BN_S_MP_MUL_DIGS_C res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */ @@ -629,7 +629,7 @@ static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) err = mp_exptmod(&tmpG, &tmpX, P, Y); mp_clear_multi(&tmpG, &tmpX, NULL); return err; -#else +#else #error mp_exptmod would always fail /* no invmod */ return MP_VAL; @@ -658,7 +658,7 @@ static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) dr = mp_reduce_is_2k(P) << 1; } #endif - + /* if the modulus is odd or dr != 0 use the montgomery method */ #ifdef BN_MP_EXPTMOD_FAST_C if (mp_isodd (P) == 1 || dr != 0) { @@ -693,7 +693,7 @@ static int mp_cmp (mp_int * a, mp_int * b) return MP_GT; } } - + /* compare digits */ if (a->sign == MP_NEG) { /* if negative compare opposite direction */ @@ -779,7 +779,7 @@ static int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c) } /* init temps */ - if ((res = mp_init_multi(&x, &y, &u, &v, + if ((res = mp_init_multi(&x, &y, &u, &v, &A, &B, &C, &D, NULL)) != MP_OKAY) { return res; } @@ -906,14 +906,14 @@ top: goto LBL_ERR; } } - + /* too big */ while (mp_cmp_mag(&C, b) != MP_LT) { if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { goto LBL_ERR; } } - + /* C is now the inverse */ mp_exch (&C, c); res = MP_OKAY; @@ -933,7 +933,7 @@ static int mp_cmp_mag (mp_int * a, mp_int * b) if (a->used > b->used) { return MP_GT; } - + if (a->used < b->used) { return MP_LT; } @@ -1199,8 +1199,8 @@ static void mp_rshd (mp_int * a, int b) /* top [offset into digits] */ top = a->dp + b; - /* this is implemented as a sliding window where - * the window is b-digits long and digits from + /* this is implemented as a sliding window where + * the window is b-digits long and digits from * the top of the window are copied to the bottom * * e.g. @@ -1218,13 +1218,13 @@ static void mp_rshd (mp_int * a, int b) *bottom++ = 0; } } - + /* remove excess digits */ a->used -= b; } -/* swap the elements of two integers, for cases where you can't simply swap the +/* swap the elements of two integers, for cases where you can't simply swap the * mp_int pointers around */ static void mp_exch (mp_int * a, mp_int * b) @@ -1237,7 +1237,7 @@ static void mp_exch (mp_int * a, mp_int * b) } -/* trim unused digits +/* trim unused digits * * This is used to ensure that leading zero digits are * trimed and the leading "used" digit will be non-zero @@ -1298,7 +1298,7 @@ static int mp_grow (mp_int * a, int size) #ifdef BN_MP_ABS_C -/* b = |a| +/* b = |a| * * Simple function copies the input and fixes the sign to positive */ @@ -1434,7 +1434,7 @@ static int mp_mul_2d (mp_int * a, int b, mp_int * c) /* set the carry to the carry bits of the current word */ r = rr; } - + /* set final carry */ if (r != 0) { c->dp[(c->used)++] = r; @@ -1446,7 +1446,7 @@ static int mp_mul_2d (mp_int * a, int b, mp_int * c) #ifdef BN_MP_INIT_MULTI_C -static int mp_init_multi(mp_int *mp, ...) +static int mp_init_multi(mp_int *mp, ...) { mp_err res = MP_OKAY; /* Assume ok until proven otherwise */ int n = 0; /* Number of ok inits */ @@ -1460,11 +1460,11 @@ static int mp_init_multi(mp_int *mp, ...) succeeded in init-ing, then return error. */ va_list clean_args; - + /* end the current list */ va_end(args); - - /* now start cleaning up */ + + /* now start cleaning up */ cur_arg = mp; va_start(clean_args, mp); while (n--) { @@ -1484,7 +1484,7 @@ static int mp_init_multi(mp_int *mp, ...) #ifdef BN_MP_CLEAR_MULTI_C -static void mp_clear_multi(mp_int *mp, ...) +static void mp_clear_multi(mp_int *mp, ...) { mp_int* next_mp = mp; va_list args; @@ -1558,7 +1558,7 @@ static int mp_count_bits (mp_int * a) /* get number of digits and add that */ r = (a->used - 1) * DIGIT_BIT; - + /* take the last digit and count the bits in it */ q = a->dp[a->used - 1]; while (q > ((mp_digit) 0)) { @@ -1628,7 +1628,7 @@ static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) } return res; } - + /* init our temps */ if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) { return res; @@ -1638,7 +1638,7 @@ static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) mp_set(&tq, 1); n = mp_count_bits(a) - mp_count_bits(b); if (((res = mp_abs(a, &ta)) != MP_OKAY) || - ((res = mp_abs(b, &tb)) != MP_OKAY) || + ((res = mp_abs(b, &tb)) != MP_OKAY) || ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) || ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) { goto LBL_ERR; @@ -1675,17 +1675,17 @@ LBL_ERR: #else -/* integer signed division. +/* integer signed division. * c*b + d == a [e.g. a/b, c=quotient, d=remainder] * HAC pp.598 Algorithm 14.20 * - * Note that the description in HAC is horribly - * incomplete. For example, it doesn't consider - * the case where digits are removed from 'x' in - * the inner loop. It also doesn't consider the + * Note that the description in HAC is horribly + * incomplete. For example, it doesn't consider + * the case where digits are removed from 'x' in + * the inner loop. It also doesn't consider the * case that y has fewer than three digits, etc.. * - * The overall algorithm is as described as + * The overall algorithm is as described as * 14.20 from HAC but fixed to treat these cases. */ static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) @@ -1775,7 +1775,7 @@ static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) continue; } - /* step 3.1 if xi == yt then set q{i-t-1} to b-1, + /* step 3.1 if xi == yt then set q{i-t-1} to b-1, * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ if (x.dp[i] == y.dp[t]) { q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1); @@ -1789,10 +1789,10 @@ static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK)); } - /* while (q{i-t-1} * (yt * b + y{t-1})) > - xi * b**2 + xi-1 * b + xi-2 - - do q{i-t-1} -= 1; + /* while (q{i-t-1} * (yt * b + y{t-1})) > + xi * b**2 + xi-1 * b + xi-2 + + do q{i-t-1} -= 1; */ q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK; do { @@ -1843,10 +1843,10 @@ static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) } } - /* now q is the quotient and x is the remainder - * [which we have to normalize] + /* now q is the quotient and x is the remainder + * [which we have to normalize] */ - + /* get sign before writing to c */ x.sign = x.used == 0 ? MP_ZPOS : a->sign; @@ -1914,7 +1914,7 @@ static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int red /* init M array */ /* init first cell */ if ((err = mp_init(&M[1])) != MP_OKAY) { - return err; + return err; } /* now init the second half of the array */ @@ -1932,7 +1932,7 @@ static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int red if ((err = mp_init (&mu)) != MP_OKAY) { goto LBL_M; } - + if (redmode == 0) { if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) { goto LBL_MU; @@ -1943,22 +1943,22 @@ static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int red goto LBL_MU; } redux = mp_reduce_2k_l; - } + } /* create M table * - * The M table contains powers of the base, + * The M table contains powers of the base, * e.g. M[x] = G**x mod P * - * The first half of the table is not + * The first half of the table is not * computed though accept for M[0] and M[1] */ if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) { goto LBL_MU; } - /* compute the value at M[1<<(winsize-1)] by squaring - * M[1] (winsize-1) times + /* compute the value at M[1<<(winsize-1)] by squaring + * M[1] (winsize-1) times */ if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { goto LBL_MU; @@ -1966,7 +1966,7 @@ static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int red for (x = 0; x < (winsize - 1); x++) { /* square it */ - if ((err = mp_sqr (&M[1 << (winsize - 1)], + if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { goto LBL_MU; } @@ -2117,18 +2117,18 @@ static int mp_sqr (mp_int * a, mp_int * b) if (a->used >= TOOM_SQR_CUTOFF) { res = mp_toom_sqr(a, b); /* Karatsuba? */ - } else + } else #endif #ifdef BN_MP_KARATSUBA_SQR_C if (a->used >= KARATSUBA_SQR_CUTOFF) { res = mp_karatsuba_sqr (a, b); - } else + } else #endif { #ifdef BN_FAST_S_MP_SQR_C /* can we use the fast comba multiplier? */ - if ((a->used * 2 + 1) < MP_WARRAY && - a->used < + if ((a->used * 2 + 1) < MP_WARRAY && + a->used < (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) { res = fast_s_mp_sqr (a, b); } else @@ -2145,7 +2145,7 @@ if (a->used >= KARATSUBA_SQR_CUTOFF) { } -/* reduces a modulo n where n is of the form 2**p - d +/* reduces a modulo n where n is of the form 2**p - d This differs from reduce_2k since "d" can be larger than a single digit. */ @@ -2153,33 +2153,33 @@ static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d) { mp_int q; int p, res; - + if ((res = mp_init(&q)) != MP_OKAY) { return res; } - - p = mp_count_bits(n); + + p = mp_count_bits(n); top: /* q = a/2**p, a = a mod 2**p */ if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { goto ERR; } - + /* q = q * d */ - if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { + if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { goto ERR; } - + /* a = a + q */ if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { goto ERR; } - + if (mp_cmp_mag(a, n) != MP_LT) { s_mp_sub(a, n, a); goto top; } - + ERR: mp_clear(&q); return res; @@ -2191,26 +2191,26 @@ static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d) { int res; mp_int tmp; - + if ((res = mp_init(&tmp)) != MP_OKAY) { return res; } - + if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { goto ERR; } - + if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) { goto ERR; } - + ERR: mp_clear(&tmp); return res; } -/* computes a = 2**b +/* computes a = 2**b * * Simple algorithm which zeroes the int, grows it then just sets one bit * as required. @@ -2243,7 +2243,7 @@ static int mp_2expt (mp_int * a, int b) static int mp_reduce_setup (mp_int * a, mp_int * b) { int res; - + if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { return res; } @@ -2251,7 +2251,7 @@ static int mp_reduce_setup (mp_int * a, mp_int * b) } -/* reduces x mod m, assumes 0 < x < m**2, mu is +/* reduces x mod m, assumes 0 < x < m**2, mu is * precomputed via mp_reduce_setup. * From HAC pp.604 Algorithm 14.42 */ @@ -2266,7 +2266,7 @@ static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) } /* q1 = x / b**(k-1) */ - mp_rshd (&q, um - 1); + mp_rshd (&q, um - 1); /* according to HAC this optimization is ok */ if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { @@ -2282,8 +2282,8 @@ static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { goto CLEANUP; } -#else - { +#else + { #error mp_reduce would always fail res = MP_VAL; goto CLEANUP; @@ -2292,7 +2292,7 @@ static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) } /* q3 = q2 / b**(k+1) */ - mp_rshd (&q, um + 1); + mp_rshd (&q, um + 1); /* x = x mod b**(k+1), quick (no division) */ if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { @@ -2326,7 +2326,7 @@ static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) goto CLEANUP; } } - + CLEANUP: mp_clear (&q); @@ -2335,7 +2335,7 @@ CLEANUP: /* multiplies |a| * |b| and only computes up to digs digits of result - * HAC pp. 595, Algorithm 14.12 Modified so you can control how + * HAC pp. 595, Algorithm 14.12 Modified so you can control how * many digits of output are created. */ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) @@ -2349,7 +2349,7 @@ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) #ifdef BN_FAST_S_MP_MUL_DIGS_C /* can we use the fast multiplier? */ if (((digs) < MP_WARRAY) && - MIN (a->used, b->used) < + MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { return fast_s_mp_mul_digs (a, b, c, digs); } @@ -2372,10 +2372,10 @@ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) /* setup some aliases */ /* copy of the digit from a used within the nested loop */ tmpx = a->dp[ix]; - + /* an alias for the destination shifted ix places */ tmpt = t.dp + ix; - + /* an alias for the digits of b */ tmpy = b->dp; @@ -2409,15 +2409,15 @@ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) #ifdef BN_FAST_S_MP_MUL_DIGS_C /* Fast (comba) multiplier * - * This is the fast column-array [comba] multiplier. It is - * designed to compute the columns of the product first - * then handle the carries afterwards. This has the effect + * This is the fast column-array [comba] multiplier. It is + * designed to compute the columns of the product first + * then handle the carries afterwards. This has the effect * of making the nested loops that compute the columns very * simple and schedulable on super-scalar processors. * - * This has been modified to produce a variable number of - * digits of output so if say only a half-product is required - * you don't have to compute the upper half (a feature + * This has been modified to produce a variable number of + * digits of output so if say only a half-product is required + * you don't have to compute the upper half (a feature * required for fast Barrett reduction). * * Based on Algorithm 14.12 on pp.595 of HAC. @@ -2441,7 +2441,7 @@ static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) /* clear the carry */ _W = 0; - for (ix = 0; ix < pa; ix++) { + for (ix = 0; ix < pa; ix++) { int tx, ty; int iy; mp_digit *tmpx, *tmpy; @@ -2454,7 +2454,7 @@ static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) tmpx = a->dp + tx; tmpy = b->dp + ty; - /* this is the number of times the loop will iterrate, essentially + /* this is the number of times the loop will iterrate, essentially while (tx++ < a->used && ty-- >= 0) { ... } */ iy = MIN(a->used-tx, ty+1); @@ -2501,8 +2501,8 @@ static int mp_init_size (mp_int * a, int size) int x; /* pad size so there are always extra digits */ - size += (MP_PREC * 2) - (size % MP_PREC); - + size += (MP_PREC * 2) - (size % MP_PREC); + /* alloc mem */ a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size); if (a->dp == NULL) { @@ -2556,7 +2556,7 @@ static int s_mp_sqr (mp_int * a, mp_int * b) /* alias for where to store the results */ tmpt = t.dp + (2*ix + 1); - + for (iy = ix + 1; iy < pa; iy++) { /* first calculate the product */ r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]); @@ -2863,24 +2863,24 @@ static int mp_mul_2(mp_int * a, mp_int * b) /* alias for source */ tmpa = a->dp; - + /* alias for dest */ tmpb = b->dp; /* carry */ r = 0; for (x = 0; x < a->used; x++) { - - /* get what will be the *next* carry bit from the - * MSB of the current digit + + /* get what will be the *next* carry bit from the + * MSB of the current digit */ rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); - + /* now shift up this digit, add in the carry [from the previous] */ *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; - - /* copy the carry that would be from the source - * digit into the next iteration + + /* copy the carry that would be from the source + * digit into the next iteration */ r = rr; } @@ -2892,8 +2892,8 @@ static int mp_mul_2(mp_int * a, mp_int * b) ++(b->used); } - /* now zero any excess digits on the destination - * that we didn't write to + /* now zero any excess digits on the destination + * that we didn't write to */ tmpb = b->dp + b->used; for (x = b->used; x < oldused; x++) { @@ -3011,7 +3011,7 @@ static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int /* determine and setup reduction code */ if (redmode == 0) { -#ifdef BN_MP_MONTGOMERY_SETUP_C +#ifdef BN_MP_MONTGOMERY_SETUP_C /* now setup montgomery */ if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) { goto LBL_M; @@ -3026,7 +3026,7 @@ static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int if (((P->used * 2 + 1) < MP_WARRAY) && P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { redux = fast_mp_montgomery_reduce; - } else + } else #endif { #ifdef BN_MP_MONTGOMERY_REDUCE_C @@ -3077,7 +3077,7 @@ static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) { goto LBL_RES; } -#else +#else err = MP_VAL; goto LBL_RES; #endif @@ -3245,10 +3245,10 @@ LBL_M: #ifdef BN_FAST_S_MP_SQR_C /* the jist of squaring... - * you do like mult except the offset of the tmpx [one that - * starts closer to zero] can't equal the offset of tmpy. + * you do like mult except the offset of the tmpx [one that + * starts closer to zero] can't equal the offset of tmpy. * So basically you set up iy like before then you min it with - * (ty-tx) so that it never happens. You double all those + * (ty-tx) so that it never happens. You double all those * you add in the inner loop After that loop you do the squares and add them in. @@ -3270,7 +3270,7 @@ static int fast_s_mp_sqr (mp_int * a, mp_int * b) /* number of output digits to produce */ W1 = 0; - for (ix = 0; ix < pa; ix++) { + for (ix = 0; ix < pa; ix++) { int tx, ty, iy; mp_word _W; mp_digit *tmpy; @@ -3291,7 +3291,7 @@ static int fast_s_mp_sqr (mp_int * a, mp_int * b) */ iy = MIN(a->used-tx, ty+1); - /* now for squaring tx can never equal ty + /* now for squaring tx can never equal ty * we halve the distance since they approach at a rate of 2x * and we have to round because odd cases need to be executed */ diff --git a/src/tls/rsa.c b/src/tls/rsa.c index 0b7b530bc37c..3525eb9919db 100644 --- a/src/tls/rsa.c +++ b/src/tls/rsa.c @@ -80,7 +80,7 @@ crypto_rsa_import_public_key(const u8 *buf, size_t len) * PKCS #1, 7.1: * RSAPublicKey ::= SEQUENCE { * modulus INTEGER, -- n - * publicExponent INTEGER -- e + * publicExponent INTEGER -- e * } */ diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c index 9bc0d211f48d..76e19746b020 100644 --- a/src/tls/tlsv1_client.c +++ b/src/tls/tlsv1_client.c @@ -264,7 +264,7 @@ failed: * @in_data: Pointer to plaintext data to be encrypted * @in_len: Input buffer length * @out_data: Pointer to output buffer (encrypted TLS data) - * @out_len: Maximum out_data length + * @out_len: Maximum out_data length * Returns: Number of bytes written to out_data, -1 on failure * * This function is used after TLS handshake has been completed successfully to diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c index 244c3cb06082..e66f1a98896d 100644 --- a/src/tls/tlsv1_client_read.c +++ b/src/tls/tlsv1_client_read.c @@ -685,10 +685,9 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, pos, conn->dh_p_len); goto fail; } - conn->dh_p = os_malloc(conn->dh_p_len); + conn->dh_p = os_memdup(pos, conn->dh_p_len); if (conn->dh_p == NULL) goto fail; - os_memcpy(conn->dh_p, pos, conn->dh_p_len); pos += conn->dh_p_len; wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)", conn->dh_p, conn->dh_p_len); @@ -700,10 +699,9 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, if (val == 0 || val > (size_t) (end - pos)) goto fail; conn->dh_g_len = val; - conn->dh_g = os_malloc(conn->dh_g_len); + conn->dh_g = os_memdup(pos, conn->dh_g_len); if (conn->dh_g == NULL) goto fail; - os_memcpy(conn->dh_g, pos, conn->dh_g_len); pos += conn->dh_g_len; wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)", conn->dh_g, conn->dh_g_len); @@ -717,10 +715,9 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, if (val == 0 || val > (size_t) (end - pos)) goto fail; conn->dh_ys_len = val; - conn->dh_ys = os_malloc(conn->dh_ys_len); + conn->dh_ys = os_memdup(pos, conn->dh_ys_len); if (conn->dh_ys == NULL) goto fail; - os_memcpy(conn->dh_ys, pos, conn->dh_ys_len); pos += conn->dh_ys_len; wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)", conn->dh_ys, conn->dh_ys_len); diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c index 6b28417e499c..e178915a454d 100644 --- a/src/tls/tlsv1_common.c +++ b/src/tls/tlsv1_common.c @@ -21,7 +21,7 @@ * RFC 2246 Section 9: Mandatory to implement TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA * Add support for commonly used cipher suites; don't bother with exportable * suites. - */ + */ static const struct tls_cipher_suite tls_cipher_suites[] = { { TLS_NULL_WITH_NULL_NULL, TLS_KEY_X_NULL, TLS_CIPHER_NULL, @@ -482,21 +482,21 @@ int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk, os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01" "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0) { - wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-256"); + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-256"); decrypted = buf + 19; buflen -= 19; } else if (buflen >= 19 + 48 && os_memcmp(buf, "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01" "\x65\x03\x04\x02\x02\x05\x00\x04\x30", 19) == 0) { - wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-384"); + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-384"); decrypted = buf + 19; buflen -= 19; } else if (buflen >= 19 + 64 && os_memcmp(buf, "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01" "\x65\x03\x04\x02\x03\x05\x00\x04\x40", 19) == 0) { - wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-512"); + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-512"); decrypted = buf + 19; buflen -= 19; diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c index 52c1ae0143da..842e5dd728c7 100644 --- a/src/tls/tlsv1_cred.c +++ b/src/tls/tlsv1_cred.c @@ -1166,10 +1166,9 @@ static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred, if (hdr.length == 0) return -1; os_free(cred->dh_p); - cred->dh_p = os_malloc(hdr.length); + cred->dh_p = os_memdup(hdr.payload, hdr.length); if (cred->dh_p == NULL) return -1; - os_memcpy(cred->dh_p, hdr.payload, hdr.length); cred->dh_p_len = hdr.length; pos = hdr.payload + hdr.length; @@ -1188,10 +1187,9 @@ static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred, if (hdr.length == 0) return -1; os_free(cred->dh_g); - cred->dh_g = os_malloc(hdr.length); + cred->dh_g = os_memdup(hdr.payload, hdr.length); if (cred->dh_g == NULL) return -1; - os_memcpy(cred->dh_g, hdr.payload, hdr.length); cred->dh_g_len = hdr.length; return 0; diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c index ba47337bcbb1..540696904095 100644 --- a/src/tls/tlsv1_server.c +++ b/src/tls/tlsv1_server.c @@ -216,7 +216,7 @@ failed: * @in_data: Pointer to plaintext data to be encrypted * @in_len: Input buffer length * @out_data: Pointer to output buffer (encrypted TLS data) - * @out_len: Maximum out_data length + * @out_len: Maximum out_data length * Returns: Number of bytes written to out_data, -1 on failure * * This function is used after TLS handshake has been completed successfully to diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c index 75f222c4f249..f80c9a358bf5 100644 --- a/src/tls/x509v3.c +++ b/src/tls/x509v3.c @@ -274,13 +274,12 @@ static int x509_parse_public_key(const u8 *buf, size_t len, */ } os_free(cert->public_key); - cert->public_key = os_malloc(hdr.length - 1); + cert->public_key = os_memdup(pos + 1, hdr.length - 1); if (cert->public_key == NULL) { wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " "public key"); return -1; } - os_memcpy(cert->public_key, pos + 1, hdr.length - 1); cert->public_key_len = hdr.length - 1; wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey", cert->public_key, cert->public_key_len); @@ -925,10 +924,9 @@ static int x509_parse_alt_name_ip(struct x509_name *name, /* iPAddress OCTET STRING */ wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len); os_free(name->ip); - name->ip = os_malloc(len); + name->ip = os_memdup(pos, len); if (name->ip == NULL) return -1; - os_memcpy(name->ip, pos, len); name->ip_len = len; return 0; } @@ -1700,14 +1698,13 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) return NULL; } os_free(cert->sign_value); - cert->sign_value = os_malloc(hdr.length - 1); + cert->sign_value = os_memdup(pos + 1, hdr.length - 1); if (cert->sign_value == NULL) { wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " "signatureValue"); x509_certificate_free(cert); return NULL; } - os_memcpy(cert->sign_value, pos + 1, hdr.length - 1); cert->sign_value_len = hdr.length - 1; wpa_hexdump(MSG_MSGDUMP, "X509: signature", cert->sign_value, cert->sign_value_len); @@ -2039,7 +2036,7 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, for (cert = chain, idx = 0; cert; cert = cert->next, idx++) { cert->issuer_trusted = 0; - x509_name_string(&cert->subject, buf, sizeof(buf)); + x509_name_string(&cert->subject, buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf); if (chain_trusted) @@ -2063,11 +2060,11 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, wpa_printf(MSG_DEBUG, "X509: Certificate " "chain issuer name mismatch"); x509_name_string(&cert->issuer, buf, - sizeof(buf)); + sizeof(buf)); wpa_printf(MSG_DEBUG, "X509: cert issuer: %s", buf); x509_name_string(&cert->next->subject, buf, - sizeof(buf)); + sizeof(buf)); wpa_printf(MSG_DEBUG, "X509: next cert " "subject: %s", buf); *reason = X509_VALIDATE_CERTIFICATE_UNKNOWN; diff --git a/src/utils/Makefile b/src/utils/Makefile index 8aad813cfc85..52efc5321fca 100644 --- a/src/utils/Makefile +++ b/src/utils/Makefile @@ -17,6 +17,7 @@ LIB_OBJS= \ base64.o \ bitfield.o \ common.o \ + crc32.o \ ip_addr.o \ radiotap.o \ trace.o \ diff --git a/src/utils/base64.c b/src/utils/base64.c index d44f290e5684..8eb4ba127d48 100644 --- a/src/utils/base64.c +++ b/src/utils/base64.c @@ -13,21 +13,14 @@ static const unsigned char base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const unsigned char base64_url_table[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; -/** - * base64_encode - Base64 encode - * @src: Data to be encoded - * @len: Length of the data to be encoded - * @out_len: Pointer to output length variable, or %NULL if not used - * Returns: Allocated buffer of out_len bytes of encoded data, - * or %NULL on failure - * - * Caller is responsible for freeing the returned buffer. Returned buffer is - * nul terminated to make it easier to use as a C string. The nul terminator is - * not included in out_len. - */ -unsigned char * base64_encode(const unsigned char *src, size_t len, - size_t *out_len) + +static unsigned char * base64_gen_encode(const unsigned char *src, size_t len, + size_t *out_len, + const unsigned char *table, + int add_pad) { unsigned char *out, *pos; const unsigned char *end, *in; @@ -35,7 +28,8 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, int line_len; olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */ - olen += olen / 72; /* line feeds */ + if (add_pad) + olen += olen / 72; /* line feeds */ olen++; /* nul termination */ if (olen < len) return NULL; /* integer overflow */ @@ -48,35 +42,35 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, pos = out; line_len = 0; while (end - in >= 3) { - *pos++ = base64_table[(in[0] >> 2) & 0x3f]; - *pos++ = base64_table[(((in[0] & 0x03) << 4) | - (in[1] >> 4)) & 0x3f]; - *pos++ = base64_table[(((in[1] & 0x0f) << 2) | - (in[2] >> 6)) & 0x3f]; - *pos++ = base64_table[in[2] & 0x3f]; + *pos++ = table[(in[0] >> 2) & 0x3f]; + *pos++ = table[(((in[0] & 0x03) << 4) | (in[1] >> 4)) & 0x3f]; + *pos++ = table[(((in[1] & 0x0f) << 2) | (in[2] >> 6)) & 0x3f]; + *pos++ = table[in[2] & 0x3f]; in += 3; line_len += 4; - if (line_len >= 72) { + if (add_pad && line_len >= 72) { *pos++ = '\n'; line_len = 0; } } if (end - in) { - *pos++ = base64_table[(in[0] >> 2) & 0x3f]; + *pos++ = table[(in[0] >> 2) & 0x3f]; if (end - in == 1) { - *pos++ = base64_table[((in[0] & 0x03) << 4) & 0x3f]; - *pos++ = '='; + *pos++ = table[((in[0] & 0x03) << 4) & 0x3f]; + if (add_pad) + *pos++ = '='; } else { - *pos++ = base64_table[(((in[0] & 0x03) << 4) | - (in[1] >> 4)) & 0x3f]; - *pos++ = base64_table[((in[1] & 0x0f) << 2) & 0x3f]; + *pos++ = table[(((in[0] & 0x03) << 4) | + (in[1] >> 4)) & 0x3f]; + *pos++ = table[((in[1] & 0x0f) << 2) & 0x3f]; } - *pos++ = '='; + if (add_pad) + *pos++ = '='; line_len += 4; } - if (line_len) + if (add_pad && line_len) *pos++ = '\n'; *pos = '\0'; @@ -86,26 +80,18 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, } -/** - * base64_decode - Base64 decode - * @src: Data to be decoded - * @len: Length of the data to be decoded - * @out_len: Pointer to output length variable - * Returns: Allocated buffer of out_len bytes of decoded data, - * or %NULL on failure - * - * Caller is responsible for freeing the returned buffer. - */ -unsigned char * base64_decode(const unsigned char *src, size_t len, - size_t *out_len) +static unsigned char * base64_gen_decode(const unsigned char *src, size_t len, + size_t *out_len, + const unsigned char *table) { unsigned char dtable[256], *out, *pos, block[4], tmp; size_t i, count, olen; int pad = 0; + size_t extra_pad; os_memset(dtable, 0x80, 256); for (i = 0; i < sizeof(base64_table) - 1; i++) - dtable[base64_table[i]] = (unsigned char) i; + dtable[table[i]] = (unsigned char) i; dtable['='] = 0; count = 0; @@ -114,21 +100,28 @@ unsigned char * base64_decode(const unsigned char *src, size_t len, count++; } - if (count == 0 || count % 4) + if (count == 0) return NULL; + extra_pad = (4 - count % 4) % 4; - olen = count / 4 * 3; + olen = (count + extra_pad) / 4 * 3; pos = out = os_malloc(olen); if (out == NULL) return NULL; count = 0; - for (i = 0; i < len; i++) { - tmp = dtable[src[i]]; + for (i = 0; i < len + extra_pad; i++) { + unsigned char val; + + if (i >= len) + val = '='; + else + val = src[i]; + tmp = dtable[val]; if (tmp == 0x80) continue; - if (src[i] == '=') + if (val == '=') pad++; block[count] = tmp; count++; @@ -155,3 +148,53 @@ unsigned char * base64_decode(const unsigned char *src, size_t len, *out_len = pos - out; return out; } + + +/** + * base64_encode - Base64 encode + * @src: Data to be encoded + * @len: Length of the data to be encoded + * @out_len: Pointer to output length variable, or %NULL if not used + * Returns: Allocated buffer of out_len bytes of encoded data, + * or %NULL on failure + * + * Caller is responsible for freeing the returned buffer. Returned buffer is + * nul terminated to make it easier to use as a C string. The nul terminator is + * not included in out_len. + */ +unsigned char * base64_encode(const unsigned char *src, size_t len, + size_t *out_len) +{ + return base64_gen_encode(src, len, out_len, base64_table, 1); +} + + +unsigned char * base64_url_encode(const unsigned char *src, size_t len, + size_t *out_len, int add_pad) +{ + return base64_gen_encode(src, len, out_len, base64_url_table, add_pad); +} + + +/** + * base64_decode - Base64 decode + * @src: Data to be decoded + * @len: Length of the data to be decoded + * @out_len: Pointer to output length variable + * Returns: Allocated buffer of out_len bytes of decoded data, + * or %NULL on failure + * + * Caller is responsible for freeing the returned buffer. + */ +unsigned char * base64_decode(const unsigned char *src, size_t len, + size_t *out_len) +{ + return base64_gen_decode(src, len, out_len, base64_table); +} + + +unsigned char * base64_url_decode(const unsigned char *src, size_t len, + size_t *out_len) +{ + return base64_gen_decode(src, len, out_len, base64_url_table); +} diff --git a/src/utils/base64.h b/src/utils/base64.h index aa21fd0fc1b7..5a72c3ebf522 100644 --- a/src/utils/base64.h +++ b/src/utils/base64.h @@ -13,5 +13,9 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, size_t *out_len); unsigned char * base64_decode(const unsigned char *src, size_t len, size_t *out_len); +unsigned char * base64_url_encode(const unsigned char *src, size_t len, + size_t *out_len, int add_pad); +unsigned char * base64_url_decode(const unsigned char *src, size_t len, + size_t *out_len); #endif /* BASE64_H */ diff --git a/src/utils/browser-wpadebug.c b/src/utils/browser-wpadebug.c index 59ba4d1e02d8..dfb4b67977f8 100644 --- a/src/utils/browser-wpadebug.c +++ b/src/utils/browser-wpadebug.c @@ -52,7 +52,7 @@ static void http_req(void *ctx, struct http_request *req) eloop_terminate(); return; } - wpabuf_put_str(resp, "User input completed"); + wpabuf_put_str(resp, "HTTP/1.1\r\n\r\nUser input completed"); if (done) { eloop_cancel_timeout(browser_timeout, NULL, NULL); @@ -97,6 +97,7 @@ int hs20_web_browser(const char *url) if (pid == 0) { /* run the external command in the child process */ char *argv[14]; + char *envp[] = { "PATH=/system/bin:/vendor/bin", NULL }; argv[0] = "browser-wpadebug"; argv[1] = "start"; @@ -113,8 +114,8 @@ int hs20_web_browser(const char *url) argv[12] = "-3"; /* USER_CURRENT_OR_SELF */ argv[13] = NULL; - execv("/system/bin/am", argv); - wpa_printf(MSG_ERROR, "execv: %s", strerror(errno)); + execve("/system/bin/am", argv, envp); + wpa_printf(MSG_ERROR, "execve: %s", strerror(errno)); exit(0); return -1; } diff --git a/src/utils/common.c b/src/utils/common.c index 04a533a05902..1eb33705bef3 100644 --- a/src/utils/common.c +++ b/src/utils/common.c @@ -1200,3 +1200,24 @@ int str_starts(const char *str, const char *start) { return os_strncmp(str, start, os_strlen(start)) == 0; } + + +/** + * rssi_to_rcpi - Convert RSSI to RCPI + * @rssi: RSSI to convert + * Returns: RCPI corresponding to the given RSSI value, or 255 if not available. + * + * It's possible to estimate RCPI based on RSSI in dBm. This calculation will + * not reflect the correct value for high rates, but it's good enough for Action + * frames which are transmitted with up to 24 Mbps rates. + */ +u8 rssi_to_rcpi(int rssi) +{ + if (!rssi) + return 255; /* not available */ + if (rssi < -110) + return 0; + if (rssi > 0) + return 220; + return (rssi + 110) * 2; +} diff --git a/src/utils/common.h b/src/utils/common.h index 77856774d215..f824d001aeab 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -53,6 +53,15 @@ static inline unsigned int bswap_32(unsigned int v) } #endif /* __APPLE__ */ +#ifdef __rtems__ +#include <rtems/endian.h> +#define __BYTE_ORDER BYTE_ORDER +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#define __BIG_ENDIAN BIG_ENDIAN +#define bswap_16 CPU_swap_u16 +#define bswap_32 CPU_swap_u32 +#endif /* __rtems__ */ + #ifdef CONFIG_NATIVE_WINDOWS #include <winsock.h> @@ -141,6 +150,7 @@ static inline unsigned int wpa_swap_32(unsigned int v) #define host_to_le32(n) (n) #define be_to_host32(n) wpa_swap_32(n) #define host_to_be32(n) wpa_swap_32(n) +#define host_to_le64(n) (n) #define WPA_BYTE_SWAP_DEFINED @@ -331,6 +341,9 @@ static inline void WPA_PUT_LE64(u8 *a, u64 val) #ifndef ETH_P_RRB #define ETH_P_RRB 0x890D #endif /* ETH_P_RRB */ +#ifndef ETH_P_OUI +#define ETH_P_OUI 0x88B7 +#endif /* ETH_P_OUI */ #ifdef __GNUC__ @@ -423,6 +436,7 @@ void perror(const char *s); #define __bitwise __attribute__((bitwise)) #else #define __force +#undef __bitwise #define __bitwise #endif @@ -552,6 +566,7 @@ int is_ctrl_char(char c); int str_starts(const char *str, const char *start); +u8 rssi_to_rcpi(int rssi); /* * gcc 4.4 ends up generating strict-aliasing warnings about some very common diff --git a/src/utils/crc32.c b/src/utils/crc32.c new file mode 100644 index 000000000000..12d9e2a7008e --- /dev/null +++ b/src/utils/crc32.c @@ -0,0 +1,85 @@ +/* + * 32-bit CRC for FCS calculation + * Copyright (c) 2010, Jouni Malinen <j@w1.fi> + * + * 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/crc32.h" + +/* + * IEEE 802.11 FCS CRC32 + * G(x) = x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + + * x^5 + x^4 + x^2 + x + 1 + */ +static const u32 crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, + 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, + 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, + 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, + 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, + 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, + 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, + 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, + 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, + 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, + 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, + 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, + 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, + 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, + 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, + 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, + 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, + 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, + 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, + 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, + 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, + 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, + 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, + 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, + 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, + 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, + 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, + 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, + 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, + 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, + 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, + 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, + 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, + 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, + 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, + 0x2d02ef8d +}; + + +u32 crc32(const u8 *frame, size_t frame_len) +{ + size_t i; + u32 crc; + + crc = 0xFFFFFFFF; + for (i = 0; i < frame_len; i++) + crc = crc32_table[(crc ^ frame[i]) & 0xff] ^ (crc >> 8); + + return ~crc; +} diff --git a/src/utils/crc32.h b/src/utils/crc32.h new file mode 100644 index 000000000000..dc31399beb63 --- /dev/null +++ b/src/utils/crc32.h @@ -0,0 +1,14 @@ +/* + * 32-bit CRC for FCS calculation + * Copyright (c) 2010, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef CRC32_H +#define CRC32_H + +u32 crc32(const u8 *frame, size_t frame_len); + +#endif /* CRC32_H */ diff --git a/src/utils/eloop.h b/src/utils/eloop.h index 97af16f0130a..04ee6d1837b6 100644 --- a/src/utils/eloop.h +++ b/src/utils/eloop.h @@ -45,16 +45,16 @@ typedef void (*eloop_sock_handler)(int sock, void *eloop_ctx, void *sock_ctx); /** * eloop_event_handler - eloop generic event callback type * @eloop_ctx: Registered callback context data (eloop_data) - * @sock_ctx: Registered callback context data (user_data) + * @user_ctx: Registered callback context data (user_data) */ -typedef void (*eloop_event_handler)(void *eloop_data, void *user_ctx); +typedef void (*eloop_event_handler)(void *eloop_ctx, void *user_ctx); /** * eloop_timeout_handler - eloop timeout event callback type * @eloop_ctx: Registered callback context data (eloop_data) - * @sock_ctx: Registered callback context data (user_data) + * @user_ctx: Registered callback context data (user_data) */ -typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx); +typedef void (*eloop_timeout_handler)(void *eloop_ctx, void *user_ctx); /** * eloop_signal_handler - eloop signal event callback type diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c index a06aae8d9b9d..58519ea8d248 100644 --- a/src/utils/http_curl.c +++ b/src/utils/http_curl.c @@ -486,12 +486,11 @@ static void add_logo(struct http_ctx *ctx, struct http_cert *hcert, return; n->hash_len = ASN1_STRING_length(hash->hashValue); - n->hash = os_malloc(n->hash_len); + n->hash = os_memdup(ASN1_STRING_data(hash->hashValue), n->hash_len); if (n->hash == NULL) { os_free(n->alg_oid); return; } - os_memcpy(n->hash, ASN1_STRING_data(hash->hashValue), n->hash_len); len = ASN1_STRING_length(uri); n->uri = os_malloc(len + 1); @@ -987,7 +986,7 @@ static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); - ssl_ctx = ssl->ctx; + ssl_ctx = SSL_get_SSL_CTX(ssl); ctx = SSL_CTX_get_app_data(ssl_ctx); wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify, preverify_ok: %d", @@ -1095,7 +1094,7 @@ static int ocsp_resp_cb(SSL *s, void *arg) { struct http_ctx *ctx = arg; const unsigned char *p; - int len, status, reason; + int len, status, reason, res; OCSP_RESPONSE *rsp; OCSP_BASICRESP *basic; OCSP_CERTID *id; @@ -1200,17 +1199,36 @@ static int ocsp_resp_cb(SSL *s, void *arg) return 0; } - id = OCSP_cert_to_id(NULL, ctx->peer_cert, ctx->peer_issuer); + id = OCSP_cert_to_id(EVP_sha256(), ctx->peer_cert, ctx->peer_issuer); if (!id) { - wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier"); + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not create OCSP certificate identifier (SHA256)"); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(rsp); ctx->last_err = "Could not create OCSP certificate identifier"; return 0; } - if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, - &this_update, &next_update)) { + res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, + &this_update, &next_update); + if (!res) { + id = OCSP_cert_to_id(NULL, ctx->peer_cert, ctx->peer_issuer); + if (!id) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not create OCSP certificate identifier (SHA1)"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + ctx->last_err = + "Could not create OCSP certificate identifier"; + return 0; + } + + res = OCSP_resp_find_status(basic, id, &status, &reason, + &produced_at, &this_update, + &next_update); + } + + if (!res) { wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s", (ctx->ocsp == MANDATORY_OCSP) ? "" : " (OCSP not required)"); diff --git a/src/utils/json.c b/src/utils/json.c new file mode 100644 index 000000000000..b9130d3a65ab --- /dev/null +++ b/src/utils/json.c @@ -0,0 +1,569 @@ +/* + * JavaScript Object Notation (JSON) parser (RFC7159) + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "base64.h" +#include "json.h" + +#define JSON_MAX_DEPTH 10 +#define JSON_MAX_TOKENS 500 + + +void json_escape_string(char *txt, size_t maxlen, const char *data, size_t len) +{ + char *end = txt + maxlen; + size_t i; + + for (i = 0; i < len; i++) { + if (txt + 4 >= end) + break; + + switch (data[i]) { + case '\"': + *txt++ = '\\'; + *txt++ = '\"'; + break; + case '\\': + *txt++ = '\\'; + *txt++ = '\\'; + break; + case '\n': + *txt++ = '\\'; + *txt++ = 'n'; + break; + case '\r': + *txt++ = '\\'; + *txt++ = 'r'; + break; + case '\t': + *txt++ = '\\'; + *txt++ = 't'; + break; + default: + if (data[i] >= 32 && data[i] <= 126) { + *txt++ = data[i]; + } else { + txt += os_snprintf(txt, end - txt, "\\u%04x", + data[i]); + } + break; + } + } + + *txt = '\0'; +} + + +static char * json_parse_string(const char **json_pos, const char *end) +{ + const char *pos = *json_pos; + char *str, *spos, *s_end; + size_t max_len, buf_len; + u8 bin[2]; + + pos++; /* skip starting quote */ + + max_len = end - pos + 1; + buf_len = max_len > 10 ? 10 : max_len; + str = os_malloc(buf_len); + if (!str) + return NULL; + spos = str; + s_end = str + buf_len; + + for (; pos < end; pos++) { + if (buf_len < max_len && s_end - spos < 3) { + char *tmp; + int idx; + + idx = spos - str; + buf_len *= 2; + if (buf_len > max_len) + buf_len = max_len; + tmp = os_realloc(str, buf_len); + if (!tmp) + goto fail; + str = tmp; + spos = str + idx; + s_end = str + buf_len; + } + + switch (*pos) { + case '\"': /* end string */ + *spos = '\0'; + /* caller will move to the next position */ + *json_pos = pos; + return str; + case '\\': + pos++; + switch (*pos) { + case '"': + case '\\': + case '/': + *spos++ = *pos; + break; + case 'n': + *spos++ = '\n'; + break; + case 'r': + *spos++ = '\r'; + break; + case 't': + *spos++ = '\t'; + break; + case 'u': + if (end - pos < 5 || + hexstr2bin(pos + 1, bin, 2) < 0 || + bin[1] == 0x00) { + wpa_printf(MSG_DEBUG, + "JSON: Invalid \\u escape"); + goto fail; + } + if (bin[0] == 0x00) { + *spos++ = bin[1]; + } else { + *spos++ = bin[0]; + *spos++ = bin[1]; + } + pos += 4; + break; + default: + wpa_printf(MSG_DEBUG, + "JSON: Unknown escape '%c'", *pos); + goto fail; + } + break; + default: + *spos++ = *pos; + break; + } + } + +fail: + os_free(str); + return NULL; +} + + +static int json_parse_number(const char **json_pos, const char *end, + int *ret_val) +{ + const char *pos = *json_pos; + size_t len; + char *str; + + for (; pos < end; pos++) { + if (*pos != '-' && (*pos < '0' || *pos > '9')) { + pos--; + break; + } + } + if (pos < *json_pos) + return -1; + len = pos - *json_pos + 1; + str = os_malloc(len + 1); + if (!str) + return -1; + os_memcpy(str, *json_pos, len); + str[len] = '\0'; + + *ret_val = atoi(str); + os_free(str); + *json_pos = pos; + return 0; +} + + +static int json_check_tree_state(struct json_token *token) +{ + if (!token) + return 0; + if (json_check_tree_state(token->child) < 0 || + json_check_tree_state(token->sibling) < 0) + return -1; + if (token->state != JSON_COMPLETED) { + wpa_printf(MSG_DEBUG, + "JSON: Unexpected token state %d (name=%s type=%d)", + token->state, token->name ? token->name : "N/A", + token->type); + return -1; + } + return 0; +} + + +static struct json_token * json_alloc_token(unsigned int *tokens) +{ + (*tokens)++; + if (*tokens > JSON_MAX_TOKENS) { + wpa_printf(MSG_DEBUG, "JSON: Maximum token limit exceeded"); + return NULL; + } + return os_zalloc(sizeof(struct json_token)); +} + + +struct json_token * json_parse(const char *data, size_t data_len) +{ + struct json_token *root = NULL, *curr_token = NULL, *token = NULL; + const char *pos, *end; + char *str; + int num; + unsigned int depth = 0; + unsigned int tokens = 0; + + pos = data; + end = data + data_len; + + for (; pos < end; pos++) { + switch (*pos) { + case '[': /* start array */ + case '{': /* start object */ + if (!curr_token) { + token = json_alloc_token(&tokens); + if (!token) + goto fail; + if (!root) + root = token; + } else if (curr_token->state == JSON_WAITING_VALUE) { + token = curr_token; + } else if (curr_token->parent && + curr_token->parent->type == JSON_ARRAY && + curr_token->parent->state == JSON_STARTED && + curr_token->state == JSON_EMPTY) { + token = curr_token; + } else { + wpa_printf(MSG_DEBUG, + "JSON: Invalid state for start array/object"); + goto fail; + } + depth++; + if (depth > JSON_MAX_DEPTH) { + wpa_printf(MSG_DEBUG, + "JSON: Max depth exceeded"); + goto fail; + } + token->type = *pos == '[' ? JSON_ARRAY : JSON_OBJECT; + token->state = JSON_STARTED; + token->child = json_alloc_token(&tokens); + if (!token->child) + goto fail; + curr_token = token->child; + curr_token->parent = token; + curr_token->state = JSON_EMPTY; + break; + case ']': /* end array */ + case '}': /* end object */ + if (!curr_token || !curr_token->parent || + curr_token->parent->state != JSON_STARTED) { + wpa_printf(MSG_DEBUG, + "JSON: Invalid state for end array/object"); + goto fail; + } + depth--; + curr_token = curr_token->parent; + if ((*pos == ']' && + curr_token->type != JSON_ARRAY) || + (*pos == '}' && + curr_token->type != JSON_OBJECT)) { + wpa_printf(MSG_DEBUG, + "JSON: Array/Object mismatch"); + goto fail; + } + if (curr_token->child->state == JSON_EMPTY && + !curr_token->child->child && + !curr_token->child->sibling) { + /* Remove pending child token since the + * array/object was empty. */ + json_free(curr_token->child); + curr_token->child = NULL; + } + curr_token->state = JSON_COMPLETED; + break; + case '\"': /* string */ + str = json_parse_string(&pos, end); + if (!str) + goto fail; + if (!curr_token) { + token = json_alloc_token(&tokens); + if (!token) + goto fail; + token->type = JSON_STRING; + token->string = str; + token->state = JSON_COMPLETED; + } else if (curr_token->parent && + curr_token->parent->type == JSON_ARRAY && + curr_token->parent->state == JSON_STARTED && + curr_token->state == JSON_EMPTY) { + curr_token->string = str; + curr_token->state = JSON_COMPLETED; + curr_token->type = JSON_STRING; + wpa_printf(MSG_MSGDUMP, + "JSON: String value: '%s'", + curr_token->string); + } else if (curr_token->state == JSON_EMPTY) { + curr_token->type = JSON_VALUE; + curr_token->name = str; + curr_token->state = JSON_STARTED; + } else if (curr_token->state == JSON_WAITING_VALUE) { + curr_token->string = str; + curr_token->state = JSON_COMPLETED; + curr_token->type = JSON_STRING; + wpa_printf(MSG_MSGDUMP, + "JSON: String value: '%s' = '%s'", + curr_token->name, + curr_token->string); + } else { + wpa_printf(MSG_DEBUG, + "JSON: Invalid state for a string"); + os_free(str); + goto fail; + } + break; + case ' ': + case '\t': + case '\r': + case '\n': + /* ignore whitespace */ + break; + case ':': /* name/value separator */ + if (!curr_token || curr_token->state != JSON_STARTED) + goto fail; + curr_token->state = JSON_WAITING_VALUE; + break; + case ',': /* member separator */ + if (!curr_token) + goto fail; + curr_token->sibling = json_alloc_token(&tokens); + if (!curr_token->sibling) + goto fail; + curr_token->sibling->parent = curr_token->parent; + curr_token = curr_token->sibling; + curr_token->state = JSON_EMPTY; + break; + case 't': /* true */ + case 'f': /* false */ + case 'n': /* null */ + if (!((end - pos >= 4 && + os_strncmp(pos, "true", 4) == 0) || + (end - pos >= 5 && + os_strncmp(pos, "false", 5) == 0) || + (end - pos >= 4 && + os_strncmp(pos, "null", 4) == 0))) { + wpa_printf(MSG_DEBUG, + "JSON: Invalid literal name"); + goto fail; + } + if (!curr_token) { + token = json_alloc_token(&tokens); + if (!token) + goto fail; + curr_token = token; + } else if (curr_token->state == JSON_WAITING_VALUE) { + wpa_printf(MSG_MSGDUMP, + "JSON: Literal name: '%s' = %c", + curr_token->name, *pos); + } else if (curr_token->parent && + curr_token->parent->type == JSON_ARRAY && + curr_token->parent->state == JSON_STARTED && + curr_token->state == JSON_EMPTY) { + wpa_printf(MSG_MSGDUMP, + "JSON: Literal name: %c", *pos); + } else { + wpa_printf(MSG_DEBUG, + "JSON: Invalid state for a literal name"); + goto fail; + } + switch (*pos) { + case 't': + curr_token->type = JSON_BOOLEAN; + curr_token->number = 1; + pos += 3; + break; + case 'f': + curr_token->type = JSON_BOOLEAN; + curr_token->number = 0; + pos += 4; + break; + case 'n': + curr_token->type = JSON_NULL; + pos += 3; + break; + } + curr_token->state = JSON_COMPLETED; + break; + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* number */ + if (json_parse_number(&pos, end, &num) < 0) + goto fail; + if (!curr_token) { + token = json_alloc_token(&tokens); + if (!token) + goto fail; + token->type = JSON_NUMBER; + token->number = num; + token->state = JSON_COMPLETED; + } else if (curr_token->state == JSON_WAITING_VALUE) { + curr_token->number = num; + curr_token->state = JSON_COMPLETED; + curr_token->type = JSON_NUMBER; + wpa_printf(MSG_MSGDUMP, + "JSON: Number value: '%s' = '%d'", + curr_token->name, + curr_token->number); + } else if (curr_token->parent && + curr_token->parent->type == JSON_ARRAY && + curr_token->parent->state == JSON_STARTED && + curr_token->state == JSON_EMPTY) { + curr_token->number = num; + curr_token->state = JSON_COMPLETED; + curr_token->type = JSON_NUMBER; + wpa_printf(MSG_MSGDUMP, + "JSON: Number value: %d", + curr_token->number); + } else { + wpa_printf(MSG_DEBUG, + "JSON: Invalid state for a number"); + goto fail; + } + break; + default: + wpa_printf(MSG_DEBUG, + "JSON: Unexpected JSON character: %c", *pos); + goto fail; + } + + if (!root) + root = token; + if (!curr_token) + curr_token = token; + } + + if (json_check_tree_state(root) < 0) { + wpa_printf(MSG_DEBUG, "JSON: Incomplete token in the tree"); + goto fail; + } + + return root; +fail: + wpa_printf(MSG_DEBUG, "JSON: Parsing failed"); + json_free(root); + return NULL; +} + + +void json_free(struct json_token *json) +{ + if (!json) + return; + json_free(json->child); + json_free(json->sibling); + os_free(json->name); + os_free(json->string); + os_free(json); +} + + +struct json_token * json_get_member(struct json_token *json, const char *name) +{ + struct json_token *token, *ret = NULL; + + if (!json || json->type != JSON_OBJECT) + return NULL; + /* Return last matching entry */ + for (token = json->child; token; token = token->sibling) { + if (token->name && os_strcmp(token->name, name) == 0) + ret = token; + } + return ret; +} + + +struct wpabuf * json_get_member_base64url(struct json_token *json, + const char *name) +{ + struct json_token *token; + unsigned char *buf; + size_t buflen; + struct wpabuf *ret; + + token = json_get_member(json, name); + if (!token || token->type != JSON_STRING) + return NULL; + buf = base64_url_decode((const unsigned char *) token->string, + os_strlen(token->string), &buflen); + if (!buf) + return NULL; + ret = wpabuf_alloc_ext_data(buf, buflen); + if (!ret) + os_free(buf); + + return ret; +} + + +static const char * json_type_str(enum json_type type) +{ + switch (type) { + case JSON_VALUE: + return "VALUE"; + case JSON_OBJECT: + return "OBJECT"; + case JSON_ARRAY: + return "ARRAY"; + case JSON_STRING: + return "STRING"; + case JSON_NUMBER: + return "NUMBER"; + case JSON_BOOLEAN: + return "BOOLEAN"; + case JSON_NULL: + return "NULL"; + } + return "??"; +} + + +static void json_print_token(struct json_token *token, int depth, + char *buf, size_t buflen) +{ + size_t len; + int ret; + + if (!token) + return; + len = os_strlen(buf); + ret = os_snprintf(buf + len, buflen - len, "[%d:%s:%s]", + depth, json_type_str(token->type), + token->name ? token->name : ""); + if (os_snprintf_error(buflen - len, ret)) { + buf[len] = '\0'; + return; + } + json_print_token(token->child, depth + 1, buf, buflen); + json_print_token(token->sibling, depth, buf, buflen); +} + + +void json_print_tree(struct json_token *root, char *buf, size_t buflen) +{ + buf[0] = '\0'; + json_print_token(root, 1, buf, buflen); +} diff --git a/src/utils/json.h b/src/utils/json.h new file mode 100644 index 000000000000..8faa95d8bf20 --- /dev/null +++ b/src/utils/json.h @@ -0,0 +1,42 @@ +/* + * JavaScript Object Notation (JSON) parser (RFC7159) + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef JSON_H +#define JSON_H + +struct json_token { + enum json_type { + JSON_VALUE, + JSON_OBJECT, + JSON_ARRAY, + JSON_STRING, + JSON_NUMBER, + JSON_BOOLEAN, + JSON_NULL, + } type; + enum json_parsing_state { + JSON_EMPTY, + JSON_STARTED, + JSON_WAITING_VALUE, + JSON_COMPLETED, + } state; + char *name; + char *string; + int number; + struct json_token *parent, *child, *sibling; +}; + +void json_escape_string(char *txt, size_t maxlen, const char *data, size_t len); +struct json_token * json_parse(const char *data, size_t data_len); +void json_free(struct json_token *json); +struct json_token * json_get_member(struct json_token *json, const char *name); +struct wpabuf * json_get_member_base64url(struct json_token *json, + const char *name); +void json_print_tree(struct json_token *root, char *buf, size_t buflen); + +#endif /* JSON_H */ diff --git a/src/utils/os.h b/src/utils/os.h index e8f0b792738a..21ba5c3ff85b 100644 --- a/src/utils/os.h +++ b/src/utils/os.h @@ -614,6 +614,18 @@ size_t os_strlcpy(char *dest, const char *src, size_t siz); */ int os_memcmp_const(const void *a, const void *b, size_t len); + +/** + * os_memdup - Allocate duplicate of passed memory chunk + * @src: Source buffer to duplicate + * @len: Length of source buffer + * Returns: %NULL if allocation failed, copy of src buffer otherwise + * + * This function allocates a memory block like os_malloc() would, and + * copies the given source buffer into it. + */ +void * os_memdup(const void *src, size_t len); + /** * os_exec - Execute an external program * @program: Path to the program diff --git a/src/utils/os_none.c b/src/utils/os_none.c index 0c3214d32ce5..e74f206a2c5a 100644 --- a/src/utils/os_none.c +++ b/src/utils/os_none.c @@ -114,6 +114,12 @@ void * os_zalloc(size_t size) } +void * os_memdup(const void *src, size_t n) +{ + return NULL; +} + + #ifdef OS_NO_C_LIB_DEFINES void * os_malloc(size_t size) { diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c index 65c6fa412f20..1894fcdb0cf2 100644 --- a/src/utils/os_unix.c +++ b/src/utils/os_unix.c @@ -80,6 +80,9 @@ int os_get_reltime(struct os_reltime *t) struct timespec ts; int res; + if (TEST_FAIL()) + return -1; + while (1) { res = clock_gettime(clock_id, &ts); if (res == 0) { @@ -505,6 +508,16 @@ int os_memcmp_const(const void *a, const void *b, size_t len) } +void * os_memdup(const void *src, size_t len) +{ + void *r = os_malloc(len); + + if (r) + os_memcpy(r, src, len); + return r; +} + + #ifdef WPA_TRACE #if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) @@ -537,6 +550,8 @@ static int testing_fail_alloc(void) i++; if (i < res && os_strcmp(func[i], "os_strdup") == 0) i++; + if (i < res && os_strcmp(func[i], "os_memdup") == 0) + i++; pos = wpa_trace_fail_func; diff --git a/src/utils/os_win32.c b/src/utils/os_win32.c index dea27b9f2ad8..f9e4b308ea5f 100644 --- a/src/utils/os_win32.c +++ b/src/utils/os_win32.c @@ -283,3 +283,13 @@ int os_exec(const char *program, const char *arg, int wait_completion) { return -1; } + + +void * os_memdup(const void *src, size_t len) +{ + void *r = os_malloc(len); + + if (r) + os_memcpy(r, src, len); + return r; +} diff --git a/src/utils/trace.c b/src/utils/trace.c index d72cf604f8e9..e0b5b0bb99f5 100644 --- a/src/utils/trace.c +++ b/src/utils/trace.c @@ -6,6 +6,10 @@ * See README for more details. */ +#ifdef WPA_TRACE_BFD +#define _GNU_SOURCE +#include <link.h> +#endif /* WPA_TRACE_BCD */ #include "includes.h" #include "common.h" @@ -25,6 +29,28 @@ static struct dl_list active_references = static char *prg_fname = NULL; static bfd *cached_abfd = NULL; static asymbol **syms = NULL; +static unsigned long start_offset; +static int start_offset_looked_up; + + +static int callback(struct dl_phdr_info *info, size_t size, void *data) +{ + /* + * dl_iterate_phdr(3): + * "The first object visited by callback is the main program." + */ + start_offset = info->dlpi_addr; + + /* + * dl_iterate_phdr(3): + * "The dl_iterate_phdr() function walks through the list of an + * application's shared objects and calls the function callback + * once for each object, until either all shared objects have + * been processed or callback returns a nonzero value." + */ + return 1; +} + static void get_prg_fname(void) { @@ -160,7 +186,7 @@ static void wpa_trace_bfd_addr(void *pc) if (abfd == NULL) return; - data.pc = (bfd_hostptr_t) pc; + data.pc = (bfd_hostptr_t) (pc - start_offset); data.found = FALSE; bfd_map_over_sections(abfd, find_addr_sect, &data); @@ -201,7 +227,7 @@ static const char * wpa_trace_bfd_addr2func(void *pc) if (abfd == NULL) return NULL; - data.pc = (bfd_hostptr_t) pc; + data.pc = (bfd_hostptr_t) (pc - start_offset); data.found = FALSE; bfd_map_over_sections(abfd, find_addr_sect, &data); @@ -233,6 +259,11 @@ static void wpa_trace_bfd_init(void) wpa_printf(MSG_INFO, "Failed to read symbols"); return; } + + if (!start_offset_looked_up) { + dl_iterate_phdr(callback, NULL); + start_offset_looked_up = 1; + } } @@ -268,7 +299,7 @@ size_t wpa_trace_calling_func(const char *buf[], size_t len) for (i = 0; i < btrace_num; i++) { struct bfd_data data; - data.pc = (bfd_hostptr_t) btrace_res[i]; + data.pc = (bfd_hostptr_t) (btrace_res[i] - start_offset); data.found = FALSE; bfd_map_over_sections(abfd, find_addr_sect, &data); diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c index abdb79c9879c..1b8ff82b4173 100644 --- a/src/utils/utils_module_tests.c +++ b/src/utils/utils_module_tests.c @@ -16,6 +16,7 @@ #include "utils/base64.h" #include "utils/ip_addr.h" #include "utils/eloop.h" +#include "utils/json.h" #include "utils/module_tests.h" @@ -839,6 +840,85 @@ static int eloop_tests(void) } +#ifdef CONFIG_JSON +struct json_test_data { + const char *json; + const char *tree; +}; + +static const struct json_test_data json_test_cases[] = { + { "{}", "[1:OBJECT:]" }, + { "[]", "[1:ARRAY:]" }, + { "{", NULL }, + { "[", NULL }, + { "}", NULL }, + { "]", NULL }, + { "[[]]", "[1:ARRAY:][2:ARRAY:]" }, + { "{\"t\":\"test\"}", "[1:OBJECT:][2:STRING:t]" }, + { "{\"t\":123}", "[1:OBJECT:][2:NUMBER:t]" }, + { "{\"t\":true}", "[1:OBJECT:][2:BOOLEAN:t]" }, + { "{\"t\":false}", "[1:OBJECT:][2:BOOLEAN:t]" }, + { "{\"t\":null}", "[1:OBJECT:][2:NULL:t]" }, + { "{\"t\":truetrue}", NULL }, + { "\"test\"", "[1:STRING:]" }, + { "123", "[1:NUMBER:]" }, + { "true", "[1:BOOLEAN:]" }, + { "false", "[1:BOOLEAN:]" }, + { "null", "[1:NULL:]" }, + { "truetrue", NULL }, + { " {\t\n\r\"a\"\n:\r1\n,\n\"b\":3\n}\n", + "[1:OBJECT:][2:NUMBER:a][2:NUMBER:b]" }, + { ",", NULL }, + { "{,}", NULL }, + { "[,]", NULL }, + { ":", NULL }, + { "{:}", NULL }, + { "[:]", NULL }, + { "{ \"\\u005c\" : \"\\u005c\" }", "[1:OBJECT:][2:STRING:\\]" }, + { "[{},{}]", "[1:ARRAY:][2:OBJECT:][2:OBJECT:]" }, + { "[1,2]", "[1:ARRAY:][2:NUMBER:][2:NUMBER:]" }, + { "[\"1\",\"2\"]", "[1:ARRAY:][2:STRING:][2:STRING:]" }, + { "[true,false]", "[1:ARRAY:][2:BOOLEAN:][2:BOOLEAN:]" }, +}; +#endif /* CONFIG_JSON */ + + +static int json_tests(void) +{ +#ifdef CONFIG_JSON + unsigned int i; + struct json_token *root; + char buf[1000]; + + wpa_printf(MSG_INFO, "JSON tests"); + + for (i = 0; i < ARRAY_SIZE(json_test_cases); i++) { + const struct json_test_data *test = &json_test_cases[i]; + int res = 0; + + root = json_parse(test->json, os_strlen(test->json)); + if ((root && !test->tree) || (!root && test->tree)) { + wpa_printf(MSG_INFO, "JSON test %u failed", i); + res = -1; + } else if (root) { + json_print_tree(root, buf, sizeof(buf)); + if (os_strcmp(buf, test->tree) != 0) { + wpa_printf(MSG_INFO, + "JSON test %u tree mismatch: %s %s", + i, buf, test->tree); + res = -1; + } + } + json_free(root); + if (res < 0) + return -1; + + } +#endif /* CONFIG_JSON */ + return 0; +} + + int utils_module_tests(void) { int ret = 0; @@ -855,6 +935,7 @@ int utils_module_tests(void) wpabuf_tests() < 0 || ip_addr_tests() < 0 || eloop_tests() < 0 || + json_tests() < 0 || int_array_tests() < 0) ret = -1; diff --git a/src/utils/uuid.c b/src/utils/uuid.c index 0f224f976b80..98e43d02f68b 100644 --- a/src/utils/uuid.c +++ b/src/utils/uuid.c @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "crypto/sha256.h" #include "uuid.h" int uuid_str2bin(const char *str, u8 *bin) @@ -69,3 +70,27 @@ int is_nil_uuid(const u8 *uuid) return 0; return 1; } + + +int uuid_random(u8 *uuid) +{ + struct os_time t; + u8 hash[SHA256_MAC_LEN]; + + /* Use HMAC-SHA256 and timestamp as context to avoid exposing direct + * os_get_random() output in the UUID field. */ + os_get_time(&t); + if (os_get_random(uuid, UUID_LEN) < 0 || + hmac_sha256(uuid, UUID_LEN, (const u8 *) &t, sizeof(t), hash) < 0) + return -1; + + os_memcpy(uuid, hash, UUID_LEN); + + /* Version: 4 = random */ + uuid[6] = (4 << 4) | (uuid[6] & 0x0f); + + /* Variant specified in RFC 4122 */ + uuid[8] = 0x80 | (uuid[8] & 0x3f); + + return 0; +} diff --git a/src/utils/uuid.h b/src/utils/uuid.h index 5e860cbc5936..6e20210f99b9 100644 --- a/src/utils/uuid.h +++ b/src/utils/uuid.h @@ -14,5 +14,6 @@ int uuid_str2bin(const char *str, u8 *bin); int uuid_bin2str(const u8 *bin, char *str, size_t max_len); int is_nil_uuid(const u8 *uuid); +int uuid_random(u8 *uuid); #endif /* UUID_H */ diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c index f7acf6b9f698..a56462b8bbdc 100644 --- a/src/utils/wpa_debug.c +++ b/src/utils/wpa_debug.c @@ -13,7 +13,7 @@ #ifdef CONFIG_DEBUG_SYSLOG #include <syslog.h> -static int wpa_debug_syslog = 0; +int wpa_debug_syslog = 0; #endif /* CONFIG_DEBUG_SYSLOG */ #ifdef CONFIG_DEBUG_LINUX_TRACING @@ -58,6 +58,10 @@ static int wpa_to_android_level(int level) #ifndef CONFIG_NO_STDOUT_DEBUG #ifdef CONFIG_DEBUG_FILE +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + static FILE *out_file = NULL; #endif /* CONFIG_DEBUG_FILE */ @@ -539,6 +543,8 @@ int wpa_debug_reopen_file(void) int wpa_debug_open_file(const char *path) { #ifdef CONFIG_DEBUG_FILE + int out_fd; + if (!path) return 0; @@ -548,10 +554,28 @@ int wpa_debug_open_file(const char *path) last_path = os_strdup(path); } - out_file = fopen(path, "a"); + out_fd = open(path, O_CREAT | O_APPEND | O_WRONLY, + S_IRUSR | S_IWUSR | S_IRGRP); + if (out_fd < 0) { + wpa_printf(MSG_ERROR, + "%s: Failed to open output file descriptor, using standard output", + __func__); + return -1; + } + +#ifdef __linux__ + if (fcntl(out_fd, F_SETFD, FD_CLOEXEC) < 0) { + wpa_printf(MSG_DEBUG, + "%s: Failed to set FD_CLOEXEC - continue without: %s", + __func__, strerror(errno)); + } +#endif /* __linux__ */ + + out_file = fdopen(out_fd, "a"); if (out_file == NULL) { wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open " "output file, using standard output"); + close(out_fd); return -1; } #ifndef _WIN32 diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h index 17d8f963802e..1fe0b7db7482 100644 --- a/src/utils/wpa_debug.h +++ b/src/utils/wpa_debug.h @@ -14,6 +14,9 @@ extern int wpa_debug_level; extern int wpa_debug_show_keys; extern int wpa_debug_timestamp; +#ifdef CONFIG_DEBUG_SYSLOG +extern int wpa_debug_syslog; +#endif /* CONFIG_DEBUG_SYSLOG */ /* Debugging function - conditional printf and hex dump. Driver wrappers can * use these for debugging purposes. */ diff --git a/src/utils/wpabuf.c b/src/utils/wpabuf.c index 96cb25cc1764..77ee47288007 100644 --- a/src/utils/wpabuf.c +++ b/src/utils/wpabuf.c @@ -244,15 +244,13 @@ struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b) if (a) len += wpabuf_len(a); - if (b) - len += wpabuf_len(b); + len += wpabuf_len(b); n = wpabuf_alloc(len); if (n) { if (a) wpabuf_put_buf(n, a); - if (b) - wpabuf_put_buf(n, b); + wpabuf_put_buf(n, b); } wpabuf_free(a); diff --git a/src/utils/xml-utils.c b/src/utils/xml-utils.c index 4916d29765f9..dae91fee46f6 100644 --- a/src/utils/xml-utils.c +++ b/src/utils/xml-utils.c @@ -246,10 +246,10 @@ static void node_to_tnds(struct xml_node_ctx *ctx, xml_node_t *out, xml_node_create_text(ctx, tnds, NULL, "Path", uri); val = get_val(ctx, node); - if (val) { - xml_node_create_text(ctx, tnds, NULL, "Value", val); - xml_node_get_text_free(ctx, val); - } + if (val || !xml_node_first_child(ctx, node)) + xml_node_create_text(ctx, tnds, NULL, "Value", + val ? val : ""); + xml_node_get_text_free(ctx, val); new_uri = add_path(uri, name); node_to_tnds(ctx, new_uri ? out : tnds, node, new_uri); diff --git a/src/wps/wps.c b/src/wps/wps.c index fade6b6905dc..8d228270ff10 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -51,12 +51,11 @@ struct wps_data * wps_init(const struct wps_config *cfg) } if (cfg->pin) { data->dev_pw_id = cfg->dev_pw_id; - data->dev_password = os_malloc(cfg->pin_len); + data->dev_password = os_memdup(cfg->pin, cfg->pin_len); if (data->dev_password == NULL) { os_free(data); return NULL; } - os_memcpy(data->dev_password, cfg->pin, cfg->pin_len); data->dev_password_len = cfg->pin_len; wpa_hexdump_key(MSG_DEBUG, "WPS: AP PIN dev_password", data->dev_password, data->dev_password_len); @@ -75,14 +74,12 @@ struct wps_data * wps_init(const struct wps_config *cfg) data->dev_pw_id = cfg->wps->ap_nfc_dev_pw_id; data->dev_password = - os_malloc(wpabuf_len(cfg->wps->ap_nfc_dev_pw)); + os_memdup(wpabuf_head(cfg->wps->ap_nfc_dev_pw), + wpabuf_len(cfg->wps->ap_nfc_dev_pw)); if (data->dev_password == NULL) { os_free(data); return NULL; } - os_memcpy(data->dev_password, - wpabuf_head(cfg->wps->ap_nfc_dev_pw), - wpabuf_len(cfg->wps->ap_nfc_dev_pw)); data->dev_password_len = wpabuf_len(cfg->wps->ap_nfc_dev_pw); wpa_hexdump_key(MSG_DEBUG, "WPS: NFC dev_password", data->dev_password, data->dev_password_len); @@ -124,15 +121,14 @@ struct wps_data * wps_init(const struct wps_config *cfg) if (cfg->new_ap_settings) { data->new_ap_settings = - os_malloc(sizeof(*data->new_ap_settings)); + os_memdup(cfg->new_ap_settings, + sizeof(*data->new_ap_settings)); if (data->new_ap_settings == NULL) { bin_clear_free(data->dev_password, data->dev_password_len); os_free(data); return NULL; } - os_memcpy(data->new_ap_settings, cfg->new_ap_settings, - sizeof(*data->new_ap_settings)); } if (cfg->peer_addr) diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c index 2e3472177de9..bcae1ba5887b 100644 --- a/src/wps/wps_common.c +++ b/src/wps/wps_common.c @@ -654,6 +654,7 @@ int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey) pub = wpabuf_zeropad(pub, 192); if (pub == NULL) { wpabuf_free(priv); + dh5_free(dh_ctx); return -1; } wpa_hexdump_buf(MSG_DEBUG, "WPS: Generated new DH pubkey", pub); diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c index b840acd924a7..affd6a4af38a 100644 --- a/src/wps/wps_er.c +++ b/src/wps/wps_er.c @@ -322,11 +322,10 @@ static int wps_er_ap_use_cached_settings(struct wps_er *er, if (!s) return -1; - ap->ap_settings = os_malloc(sizeof(*ap->ap_settings)); + ap->ap_settings = os_memdup(&s->ap_settings, sizeof(*ap->ap_settings)); if (ap->ap_settings == NULL) return -1; - os_memcpy(ap->ap_settings, &s->ap_settings, sizeof(*ap->ap_settings)); wpa_printf(MSG_DEBUG, "WPS ER: Use cached AP settings"); return 0; } @@ -1958,10 +1957,9 @@ int wps_er_set_config(struct wps_er *er, const u8 *uuid, const u8 *addr, } os_free(ap->ap_settings); - ap->ap_settings = os_malloc(sizeof(*cred)); + ap->ap_settings = os_memdup(cred, sizeof(*cred)); if (ap->ap_settings == NULL) return -1; - os_memcpy(ap->ap_settings, cred, sizeof(*cred)); ap->ap_settings->cred_attr = NULL; wpa_printf(MSG_DEBUG, "WPS ER: Updated local AP settings based set " "config request"); @@ -2018,10 +2016,9 @@ int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *addr, } os_free(ap->ap_settings); - ap->ap_settings = os_malloc(sizeof(*cred)); + ap->ap_settings = os_memdup(cred, sizeof(*cred)); if (ap->ap_settings == NULL) return -1; - os_memcpy(ap->ap_settings, cred, sizeof(*cred)); ap->ap_settings->cred_attr = NULL; if (wps_er_send_get_device_info(ap, wps_er_ap_config_m1) < 0) diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index fac8bd837f2f..379925e3f0a9 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -748,12 +748,11 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr, p->wildcard_uuid = 1; else os_memcpy(p->uuid, uuid, WPS_UUID_LEN); - p->pin = os_malloc(pin_len); + p->pin = os_memdup(pin, pin_len); if (p->pin == NULL) { os_free(p); return -1; } - os_memcpy(p->pin, pin, pin_len); p->pin_len = pin_len; if (timeout) { @@ -881,6 +880,7 @@ static const u8 * wps_registrar_get_pin(struct wps_registrar *reg, const u8 *uuid, size_t *pin_len) { struct wps_uuid_pin *pin, *found = NULL; + int wildcard = 0; wps_registrar_expire_pins(reg); @@ -900,7 +900,7 @@ static const u8 * wps_registrar_get_pin(struct wps_registrar *reg, pin->wildcard_uuid == 2) { wpa_printf(MSG_DEBUG, "WPS: Found a wildcard " "PIN. Assigned it for this UUID-E"); - pin->wildcard_uuid++; + wildcard = 1; os_memcpy(pin->uuid, uuid, WPS_UUID_LEN); found = pin; break; @@ -922,6 +922,8 @@ static const u8 * wps_registrar_get_pin(struct wps_registrar *reg, } *pin_len = found->pin_len; found->flags |= PIN_LOCKED; + if (wildcard) + found->wildcard_uuid++; return found->pin; } @@ -1404,10 +1406,9 @@ static int wps_get_dev_password(struct wps_data *wps) return -1; } - wps->dev_password = os_malloc(pin_len); + wps->dev_password = os_memdup(pin, pin_len); if (wps->dev_password == NULL) return -1; - os_memcpy(wps->dev_password, pin, pin_len); wps->dev_password_len = pin_len; return 0; |
