diff options
Diffstat (limited to 'src')
229 files changed, 13057 insertions, 3963 deletions
diff --git a/src/Makefile b/src/Makefile index 10e0171b0c6df..c9e84c11de7a4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,4 +1,5 @@ SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p pae radius rsn_supp tls utils wps +SUBDIRS += fst all: for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done diff --git a/src/ap/Makefile b/src/ap/Makefile index adfd3dfd5b9be..98788fef797ed 100644 --- a/src/ap/Makefile +++ b/src/ap/Makefile @@ -1,8 +1,67 @@ -all: - @echo Nothing to be made. +all: libap.a clean: - rm -f *~ *.o *.d *.gcno *.gcda *.gcov + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libap.a install: @echo Nothing to be made. + +include ../lib.rules + +CFLAGS += -DHOSTAPD +CFLAGS += -DNEED_AP_MLME +CFLAGS += -DCONFIG_HS20 +CFLAGS += -DCONFIG_INTERWORKING +CFLAGS += -DCONFIG_IEEE80211R +CFLAGS += -DCONFIG_IEEE80211W +CFLAGS += -DCONFIG_WPS +CFLAGS += -DCONFIG_PROXYARP +CFLAGS += -DCONFIG_IAPP + +LIB_OBJS= \ + accounting.o \ + ap_config.o \ + ap_drv_ops.o \ + ap_list.o \ + ap_mlme.o \ + authsrv.o \ + beacon.o \ + bss_load.o \ + ctrl_iface_ap.o \ + dfs.o \ + dhcp_snoop.o \ + drv_callbacks.o \ + eap_user_db.o \ + gas_serv.o \ + hostapd.o \ + hs20.o \ + hw_features.o \ + iapp.o \ + ieee802_11_auth.o \ + ieee802_11.o \ + ieee802_11_ht.o \ + ieee802_11_shared.o \ + ieee802_11_vht.o \ + ieee802_1x.o \ + ndisc_snoop.o \ + p2p_hostapd.o \ + peerkey_auth.o \ + pmksa_cache_auth.o \ + preauth_auth.o \ + sta_info.o \ + tkip_countermeasures.o \ + utils.o \ + vlan_init.o \ + wmm.o \ + wnm_ap.o \ + wpa_auth.o \ + wpa_auth_ft.o \ + wpa_auth_glue.o \ + wpa_auth_ie.o \ + wps_hostapd.o \ + x_snoop.o + +libap.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/src/ap/accounting.c b/src/ap/accounting.c index 7c55146b2c5c2..a096de4d3e514 100644 --- a/src/ap/accounting.c +++ b/src/ap/accounting.c @@ -459,10 +459,14 @@ int accounting_init(struct hostapd_data *hapd) { struct os_time now; - /* Acct-Session-Id should be unique over reboots. If reliable clock is - * not available, this could be replaced with reboot counter, etc. */ + /* Acct-Session-Id should be unique over reboots. Using a random number + * is preferred. If that is not available, take the current time. Mix + * in microseconds to make this more likely to be unique. */ os_get_time(&now); - hapd->acct_session_id_hi = now.sec; + if (os_get_random((u8 *) &hapd->acct_session_id_hi, + sizeof(hapd->acct_session_id_hi)) < 0) + hapd->acct_session_id_hi = now.sec; + hapd->acct_session_id_hi ^= now.usec; if (radius_client_register(hapd->radius, RADIUS_ACCT, accounting_receive, hapd)) @@ -475,7 +479,7 @@ int accounting_init(struct hostapd_data *hapd) /** - * accounting_deinit: Deinitilize accounting + * accounting_deinit: Deinitialize accounting * @hapd: hostapd BSS data */ void accounting_deinit(struct hostapd_data *hapd) diff --git a/src/ap/acs.c b/src/ap/acs.c index ae7f6c3092899..03d797fe8836a 100644 --- a/src/ap/acs.c +++ b/src/ap/acs.c @@ -479,16 +479,10 @@ static int acs_usable_chan(struct hostapd_channel_data *chan) static int is_in_chanlist(struct hostapd_iface *iface, struct hostapd_channel_data *chan) { - int *entry; - - if (!iface->conf->chanlist) + if (!iface->conf->acs_ch_list.num) return 1; - for (entry = iface->conf->chanlist; *entry != -1; entry++) { - if (*entry == chan->chan) - return 1; - } - return 0; + return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan); } @@ -900,6 +894,9 @@ static int acs_request_scan(struct hostapd_iface *iface) if (chan->flag & HOSTAPD_CHAN_DISABLED) continue; + if (!is_in_chanlist(iface, chan)) + continue; + *freq++ = chan->freq; } *freq = 0; diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 76011dc07fd4b..9a96e50b7385b 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -172,6 +172,7 @@ struct hostapd_config * hostapd_config_defaults(void) conf->ap_table_max_size = 255; conf->ap_table_expiration_time = 60; + conf->track_sta_max_age = 180; #ifdef CONFIG_TESTING_OPTIONS conf->ignore_probe_probability = 0.0; @@ -181,6 +182,8 @@ struct hostapd_config * hostapd_config_defaults(void) conf->corrupt_gtk_rekey_mic_probability = 0.0; #endif /* CONFIG_TESTING_OPTIONS */ + conf->acs = 0; + conf->acs_ch_list.num = 0; #ifdef CONFIG_ACS conf->acs_num_scans = 5; #endif /* CONFIG_ACS */ @@ -559,6 +562,13 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->server_id); +#ifdef CONFIG_TESTING_OPTIONS + wpabuf_free(conf->own_ie_override); +#endif /* CONFIG_TESTING_OPTIONS */ + + os_free(conf->no_probe_resp_if_seen_on); + os_free(conf->no_auth_if_seen_on); + os_free(conf); } @@ -579,7 +589,7 @@ void hostapd_config_free(struct hostapd_config *conf) os_free(conf->bss); os_free(conf->supported_rates); os_free(conf->basic_rates); - os_free(conf->chanlist); + os_free(conf->acs_ch_list.range); os_free(conf->driver_params); #ifdef CONFIG_ACS os_free(conf->acs_chan_bias); @@ -817,9 +827,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))) { + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) { wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without " - "WPA2/CCMP forced WPS to be disabled"); + "WPA2/CCMP/GCMP forced WPS to be disabled"); bss->wps_state = 0; } #endif /* CONFIG_WPS */ @@ -841,6 +851,29 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, } +static int hostapd_config_check_cw(struct hostapd_config *conf, int queue) +{ + int tx_cwmin = conf->tx_queue[queue].cwmin; + int tx_cwmax = conf->tx_queue[queue].cwmax; + int ac_cwmin = conf->wmm_ac_params[queue].cwmin; + int ac_cwmax = conf->wmm_ac_params[queue].cwmax; + + if (tx_cwmin > tx_cwmax) { + wpa_printf(MSG_ERROR, + "Invalid TX queue cwMin/cwMax values. cwMin(%d) greater than cwMax(%d)", + tx_cwmin, tx_cwmax); + return -1; + } + if (ac_cwmin > ac_cwmax) { + wpa_printf(MSG_ERROR, + "Invalid WMM AC cwMin/cwMax values. cwMin(%d) greater than cwMax(%d)", + ac_cwmin, ac_cwmax); + return -1; + } + return 0; +} + + int hostapd_config_check(struct hostapd_config *conf, int full_config) { size_t i; @@ -870,6 +903,11 @@ int hostapd_config_check(struct hostapd_config *conf, int full_config) return -1; } + for (i = 0; i < NUM_TX_QUEUES; i++) { + if (hostapd_config_check_cw(conf, i)) + return -1; + } + for (i = 0; i < conf->num_bss; i++) { if (hostapd_config_check_bss(conf->bss[i], conf, full_config)) return -1; @@ -937,10 +975,11 @@ void hostapd_set_security_params(struct hostapd_bss_config *bss, bss->rsn_pairwise = WPA_CIPHER_CCMP; } else { bss->ssid.security_policy = SECURITY_PLAINTEXT; - bss->wpa_group = WPA_CIPHER_NONE; - bss->wpa_pairwise = WPA_CIPHER_NONE; - bss->rsn_pairwise = WPA_CIPHER_NONE; - if (full_config) + if (full_config) { + bss->wpa_group = WPA_CIPHER_NONE; + bss->wpa_pairwise = WPA_CIPHER_NONE; + bss->rsn_pairwise = WPA_CIPHER_NONE; bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE; + } } } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 961d2dd389f87..de470a969b500 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -1,6 +1,6 @@ /* * hostapd / Configuration definitions and helpers functions - * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -12,8 +12,10 @@ #include "common/defs.h" #include "ip_addr.h" #include "common/wpa_common.h" +#include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "wps/wps.h" +#include "fst/fst.h" /** * mesh_conf - local MBSS state and settings @@ -31,8 +33,8 @@ struct mesh_conf { u8 mesh_sp_id; /* Authentication Protocol Identifier */ u8 mesh_auth_id; - u8 *ies; - int ie_len; + u8 *rsn_ie; + int rsn_ie_len; #define MESH_CONF_SEC_NONE BIT(0) #define MESH_CONF_SEC_AUTH BIT(1) #define MESH_CONF_SEC_AMPE BIT(2) @@ -57,8 +59,6 @@ struct hostapd_radius_servers; struct ft_remote_r0kh; struct ft_remote_r1kh; -#define HOSTAPD_MAX_SSID_LEN 32 - #define NUM_WEP_KEYS 4 struct hostapd_wep_keys { u8 idx; @@ -78,7 +78,7 @@ typedef enum hostap_security_policy { } secpolicy; struct hostapd_ssid { - u8 ssid[HOSTAPD_MAX_SSID_LEN]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; unsigned int ssid_set:1; unsigned int utf8_ssid:1; @@ -114,12 +114,10 @@ struct hostapd_vlan { struct hostapd_vlan *next; int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */ char ifname[IFNAMSIZ + 1]; + int configured; int dynamic_vlan; #ifdef CONFIG_FULL_DYNAMIC_VLAN -#define DVLAN_CLEAN_BR 0x1 -#define DVLAN_CLEAN_VLAN 0x2 -#define DVLAN_CLEAN_VLAN_PORT 0x4 #define DVLAN_CLEAN_WLAN_PORT 0x8 int clean; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ @@ -332,6 +330,7 @@ struct hostapd_bss_config { char *private_key; char *private_key_passwd; int check_crl; + unsigned int tls_session_lifetime; char *ocsp_stapling_response; char *dh_file; char *openssl_ciphers; @@ -490,6 +489,7 @@ struct hostapd_bss_config { int osen; int proxy_arp; + int na_mcast_to_ucast; #ifdef CONFIG_HS20 int hs20; int disable_dgaf; @@ -510,7 +510,7 @@ struct hostapd_bss_config { char file[256]; } *hs20_icons; size_t hs20_icons_count; - u8 osu_ssid[HOSTAPD_MAX_SSID_LEN]; + u8 osu_ssid[SSID_MAX_LEN]; size_t osu_ssid_len; struct hs20_osu_provider { unsigned int friendly_name_count; @@ -545,6 +545,7 @@ struct hostapd_bss_config { #ifdef CONFIG_TESTING_OPTIONS u8 bss_load_test[5]; u8 bss_load_test_set; + struct wpabuf *own_ie_override; #endif /* CONFIG_TESTING_OPTIONS */ #define MESH_ENABLED BIT(0) @@ -553,6 +554,9 @@ struct hostapd_bss_config { int radio_measurements; int vendor_vht; + + char *no_probe_resp_if_seen_on; + char *no_auth_if_seen_on; }; @@ -568,7 +572,8 @@ struct hostapd_config { int fragm_threshold; u8 send_probe_response; u8 channel; - int *chanlist; + u8 acs; + struct wpa_freq_range_list acs_ch_list; enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */ enum { LONG_PREAMBLE = 0, @@ -584,6 +589,9 @@ struct hostapd_config { int ap_table_max_size; int ap_table_expiration_time; + unsigned int track_sta_max_num; + unsigned int track_sta_max_age; + char country[3]; /* first two octets: country code as described in * ISO/IEC 3166-1. Third octet: * ' ' (ascii 32): all environments @@ -620,6 +628,7 @@ struct hostapd_config { u16 ht_capab; int ieee80211n; int secondary_channel; + int no_pri_sec_switch; int require_ht; int obss_interval; u32 vht_capab; @@ -629,6 +638,10 @@ struct hostapd_config { u8 vht_oper_centr_freq_seg0_idx; u8 vht_oper_centr_freq_seg1_idx; +#ifdef CONFIG_FST + struct fst_iface_cfg fst_cfg; +#endif /* CONFIG_FST */ + #ifdef CONFIG_P2P u8 p2p_go_ctwindow; #endif /* CONFIG_P2P */ diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index e16306c4e1065..6cafcb7493511 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -81,6 +81,22 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, wpabuf_put_data(proberesp, buf, pos - buf); } +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) { + size_t add = wpabuf_len(hapd->iface->fst_ies); + + if (wpabuf_resize(&beacon, add) < 0) + goto fail; + wpabuf_put_buf(beacon, hapd->iface->fst_ies); + if (wpabuf_resize(&proberesp, add) < 0) + goto fail; + wpabuf_put_buf(proberesp, hapd->iface->fst_ies); + if (wpabuf_resize(&assocresp, add) < 0) + goto fail; + wpabuf_put_buf(assocresp, hapd->iface->fst_ies); + } +#endif /* CONFIG_FST */ + if (hapd->wps_beacon_ie) { if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) < 0) @@ -217,6 +233,15 @@ void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, } +int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL) + return 0; + + return hapd->driver->set_ap_wps_ie(hapd->drv_priv, NULL, NULL, NULL); +} + + int hostapd_set_ap_wps_ie(struct hostapd_data *hapd) { struct wpabuf *beacon, *proberesp, *assocresp; @@ -281,8 +306,14 @@ int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname, params.wpa = hapd->conf->wpa; params.ieee802_1x = hapd->conf->ieee802_1x; params.wpa_group = hapd->conf->wpa_group; - params.wpa_pairwise = hapd->conf->wpa_pairwise | - hapd->conf->rsn_pairwise; + if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) == + (WPA_PROTO_WPA | WPA_PROTO_RSN)) + params.wpa_pairwise = hapd->conf->wpa_pairwise | + hapd->conf->rsn_pairwise; + else if (hapd->conf->wpa & WPA_PROTO_RSN) + params.wpa_pairwise = hapd->conf->rsn_pairwise; + else if (hapd->conf->wpa & WPA_PROTO_WPA) + params.wpa_pairwise = hapd->conf->wpa_pairwise; params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt; params.rsn_preauth = hapd->conf->rsn_preauth; #ifdef CONFIG_IEEE80211W @@ -618,7 +649,7 @@ int hostapd_drv_send_mlme(struct hostapd_data *hapd, { if (hapd->driver == NULL || hapd->driver->send_mlme == NULL) return 0; - return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack); + return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0); } @@ -712,16 +743,100 @@ int hostapd_drv_set_qos_map(struct hostapd_data *hapd, } +static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd, + struct hostapd_hw_modes *mode, + int acs_ch_list_all, + int **freq_list) +{ + int i; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + + if ((acs_ch_list_all || + freq_range_list_includes(&hapd->iface->conf->acs_ch_list, + chan->chan)) && + !(chan->flag & HOSTAPD_CHAN_DISABLED)) + int_array_add_unique(freq_list, chan->freq); + } +} + + int hostapd_drv_do_acs(struct hostapd_data *hapd) { struct drv_acs_params params; + int ret, i, acs_ch_list_all = 0; + u8 *channels = NULL; + unsigned int num_channels = 0; + struct hostapd_hw_modes *mode; + int *freq_list = NULL; if (hapd->driver == NULL || hapd->driver->do_acs == NULL) return 0; + os_memset(¶ms, 0, sizeof(params)); params.hw_mode = hapd->iface->conf->hw_mode; + + /* + * If no chanlist config parameter is provided, include all enabled + * channels of the selected hw_mode. + */ + if (!hapd->iface->conf->acs_ch_list.num) + acs_ch_list_all = 1; + + mode = hapd->iface->current_mode; + if (mode) { + channels = os_malloc(mode->num_channels); + if (channels == NULL) + return -1; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + if (!acs_ch_list_all && + !freq_range_list_includes( + &hapd->iface->conf->acs_ch_list, + chan->chan)) + continue; + if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) { + channels[num_channels++] = chan->chan; + int_array_add_unique(&freq_list, chan->freq); + } + } + } else { + for (i = 0; i < hapd->iface->num_hw_features; i++) { + mode = &hapd->iface->hw_features[i]; + hostapd_get_hw_mode_any_channels(hapd, mode, + acs_ch_list_all, + &freq_list); + } + } + + params.ch_list = channels; + params.ch_list_len = num_channels; + params.freq_list = freq_list; + params.ht_enabled = !!(hapd->iface->conf->ieee80211n); params.ht40_enabled = !!(hapd->iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET); - return hapd->driver->do_acs(hapd->drv_priv, ¶ms); + params.vht_enabled = !!(hapd->iface->conf->ieee80211ac); + params.ch_width = 20; + if (hapd->iface->conf->ieee80211n && params.ht40_enabled) + params.ch_width = 40; + + /* Note: VHT20 is defined by combination of ht_capab & vht_oper_chwidth + */ + if (hapd->iface->conf->ieee80211ac && params.ht40_enabled) { + if (hapd->iface->conf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ) + params.ch_width = 80; + else if (hapd->iface->conf->vht_oper_chwidth == + VHT_CHANWIDTH_160MHZ || + hapd->iface->conf->vht_oper_chwidth == + VHT_CHANWIDTH_80P80MHZ) + params.ch_width = 160; + } + + ret = hapd->driver->do_acs(hapd->drv_priv, ¶ms); + os_free(channels); + + return ret; } diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 5d07e71f1bf13..82eaf3f08bb5b 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -24,6 +24,7 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon, struct wpabuf *proberesp, struct wpabuf *assocresp); +int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd); int hostapd_set_ap_wps_ie(struct hostapd_data *hapd); int hostapd_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized); diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c index 04a56a95efd91..8bf6ddec8d375 100644 --- a/src/ap/ap_list.c +++ b/src/ap/ap_list.c @@ -193,14 +193,14 @@ void ap_list_process_beacon(struct hostapd_iface *iface, elems->supp_rates, elems->supp_rates_len, elems->ext_supp_rates, elems->ext_supp_rates_len); - if (elems->erp_info && elems->erp_info_len == 1) + if (elems->erp_info) ap->erp = elems->erp_info[0]; else ap->erp = -1; - if (elems->ds_params && elems->ds_params_len == 1) + if (elems->ds_params) ap->channel = elems->ds_params[0]; - else if (elems->ht_operation && elems->ht_operation_len >= 1) + else if (elems->ht_operation) ap->channel = elems->ht_operation[0]; else if (fi) ap->channel = fi->channel; @@ -248,15 +248,12 @@ void ap_list_process_beacon(struct hostapd_iface *iface, } -static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) +void ap_list_timer(struct hostapd_iface *iface) { - struct hostapd_iface *iface = eloop_ctx; struct os_reltime now; struct ap_info *ap; int set_beacon = 0; - eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); - if (!iface->ap_list) return; @@ -305,13 +302,11 @@ static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) int ap_list_init(struct hostapd_iface *iface) { - eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); return 0; } void ap_list_deinit(struct hostapd_iface *iface) { - eloop_cancel_timeout(ap_list_timer, iface, NULL); hostapd_free_aps(iface); } diff --git a/src/ap/ap_list.h b/src/ap/ap_list.h index 93dc0eda88d33..9e0353cfec95a 100644 --- a/src/ap/ap_list.h +++ b/src/ap/ap_list.h @@ -39,6 +39,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface, #ifdef NEED_AP_MLME int ap_list_init(struct hostapd_iface *iface); void ap_list_deinit(struct hostapd_iface *iface); +void ap_list_timer(struct hostapd_iface *iface); #else /* NEED_AP_MLME */ static inline int ap_list_init(struct hostapd_iface *iface) { @@ -48,6 +49,10 @@ static inline int ap_list_init(struct hostapd_iface *iface) static inline void ap_list_deinit(struct hostapd_iface *iface) { } + +static inline void ap_list_timer(struct hostapd_iface *iface) +{ +} #endif /* NEED_AP_MLME */ #endif /* AP_LIST_H */ diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index bd1778e418651..934dcfc8d6318 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -55,10 +55,11 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, { const struct hostapd_eap_user *eap_user; int i; + int rv = -1; eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2); if (eap_user == NULL) - return -1; + goto out; if (user == NULL) return 0; @@ -72,7 +73,7 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, if (eap_user->password) { user->password = os_malloc(eap_user->password_len); if (user->password == NULL) - return -1; + goto out; os_memcpy(user->password, eap_user->password, eap_user->password_len); user->password_len = eap_user->password_len; @@ -83,8 +84,13 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, user->ttls_auth = eap_user->ttls_auth; user->remediation = eap_user->remediation; user->accept_attr = eap_user->accept_attr; + rv = 0; - return 0; +out: + if (rv) + wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__); + + return rv; } @@ -126,6 +132,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) #endif /* CONFIG_HS20 */ srv.erp = conf->eap_server_erp; srv.erp_domain = conf->erp_domain; + srv.tls_session_lifetime = conf->tls_session_lifetime; hapd->radius_srv = radius_server_init(&srv); if (hapd->radius_srv == NULL) { @@ -145,9 +152,12 @@ int authsrv_init(struct hostapd_data *hapd) if (hapd->conf->eap_server && (hapd->conf->ca_cert || hapd->conf->server_cert || hapd->conf->private_key || hapd->conf->dh_file)) { + struct tls_config conf; struct tls_connection_params params; - hapd->ssl_ctx = tls_init(NULL); + os_memset(&conf, 0, sizeof(conf)); + conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; + hapd->ssl_ctx = tls_init(&conf); if (hapd->ssl_ctx == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize TLS"); authsrv_deinit(hapd); diff --git a/src/ap/beacon.c b/src/ap/beacon.c index e575b65cbf3ac..5fe8fd5660b41 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -360,7 +360,6 @@ static u8 * hostapd_add_csa_elems(struct hostapd_data *hapd, u8 *pos, static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, - struct sta_info *sta, const struct ieee80211_mgmt *req, int is_p2p, size_t *resp_len) { @@ -378,6 +377,10 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, if (hapd->p2p_probe_resp_ie) buflen += wpabuf_len(hapd->p2p_probe_resp_ie); #endif /* CONFIG_P2P */ +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) + buflen += wpabuf_len(hapd->iface->fst_ies); +#endif /* CONFIG_FST */ if (hapd->conf->vendor_elements) buflen += wpabuf_len(hapd->conf->vendor_elements); if (hapd->conf->vendor_vht) { @@ -402,7 +405,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, /* hardware or low-level driver will setup seq_ctrl and timestamp */ resp->u.probe_resp.capab_info = - host_to_le16(hostapd_own_capab_info(hapd, sta, 1)); + host_to_le16(hostapd_own_capab_info(hapd)); pos = resp->u.probe_resp.variable; *pos++ = WLAN_EID_SSID; @@ -450,6 +453,15 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp, &hapd->cs_c_off_proberesp); + +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) { + os_memcpy(pos, wpabuf_head(hapd->iface->fst_ies), + wpabuf_len(hapd->iface->fst_ies)); + pos += wpabuf_len(hapd->iface->fst_ies); + } +#endif /* CONFIG_FST */ + #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { pos = hostapd_eid_vht_capabilities(hapd, pos); @@ -540,6 +552,102 @@ static enum ssid_match_result ssid_match(struct hostapd_data *hapd, } +void sta_track_expire(struct hostapd_iface *iface, int force) +{ + struct os_reltime now; + struct hostapd_sta_info *info; + + if (!iface->num_sta_seen) + return; + + os_get_reltime(&now); + while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info, + list))) { + if (!force && + !os_reltime_expired(&now, &info->last_seen, + iface->conf->track_sta_max_age)) + break; + force = 0; + + wpa_printf(MSG_MSGDUMP, "%s: Expire STA tracking entry for " + MACSTR, iface->bss[0]->conf->iface, + MAC2STR(info->addr)); + dl_list_del(&info->list); + iface->num_sta_seen--; + os_free(info); + } +} + + +static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface, + const u8 *addr) +{ + struct hostapd_sta_info *info; + + dl_list_for_each(info, &iface->sta_seen, struct hostapd_sta_info, list) + if (os_memcmp(addr, info->addr, ETH_ALEN) == 0) + return info; + + return NULL; +} + + +void sta_track_add(struct hostapd_iface *iface, const u8 *addr) +{ + struct hostapd_sta_info *info; + + info = sta_track_get(iface, addr); + if (info) { + /* Move the most recent entry to the end of the list */ + dl_list_del(&info->list); + dl_list_add_tail(&iface->sta_seen, &info->list); + os_get_reltime(&info->last_seen); + return; + } + + /* Add a new entry */ + info = os_zalloc(sizeof(*info)); + os_memcpy(info->addr, addr, ETH_ALEN); + os_get_reltime(&info->last_seen); + + if (iface->num_sta_seen >= iface->conf->track_sta_max_num) { + /* Expire oldest entry to make room for a new one */ + sta_track_expire(iface, 1); + } + + wpa_printf(MSG_MSGDUMP, "%s: Add STA tracking entry for " + MACSTR, iface->bss[0]->conf->iface, MAC2STR(addr)); + dl_list_add_tail(&iface->sta_seen, &info->list); + iface->num_sta_seen++; +} + + +struct hostapd_data * +sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr, + const char *ifname) +{ + struct hapd_interfaces *interfaces = iface->interfaces; + size_t i, j; + + for (i = 0; i < interfaces->count; i++) { + struct hostapd_data *hapd = NULL; + + iface = interfaces->iface[i]; + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + if (os_strcmp(ifname, hapd->conf->iface) == 0) + break; + hapd = NULL; + } + + if (hapd && sta_track_get(iface, addr)) + return hapd; + } + + return NULL; +} + + void handle_probe_req(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ssi_signal) @@ -548,7 +656,6 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee802_11_elems elems; const u8 *ie; size_t ie_len; - struct sta_info *sta = NULL; size_t i, resp_len; int noack; enum ssid_match_result res; @@ -556,6 +663,8 @@ void handle_probe_req(struct hostapd_data *hapd, ie = mgmt->u.probe_req.variable; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) return; + if (hapd->iconf->track_sta_max_num) + sta_track_add(hapd->iface, mgmt->sa); ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) @@ -590,7 +699,7 @@ void handle_probe_req(struct hostapd_data *hapd, * is less likely to see them (Probe Request frame sent on a * neighboring, but partially overlapping, channel). */ - if (elems.ds_params && elems.ds_params_len == 1 && + if (elems.ds_params && hapd->iface->current_mode && (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G || hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) && @@ -635,8 +744,6 @@ void handle_probe_req(struct hostapd_data *hapd, return; } - sta = ap_get_sta(hapd, mgmt->sa); - #ifdef CONFIG_P2P if ((hapd->conf->p2p & P2P_GROUP_OWNER) && elems.ssid_len == P2P_WILDCARD_SSID_LEN && @@ -649,10 +756,7 @@ void handle_probe_req(struct hostapd_data *hapd, res = ssid_match(hapd, elems.ssid, elems.ssid_len, elems.ssid_list, elems.ssid_list_len); - if (res != NO_SSID_MATCH) { - if (sta) - sta->ssid_probe = &hapd->conf->ssid; - } else { + if (res == NO_SSID_MATCH) { if (!(mgmt->da[0] & 0x01)) { wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for foreign SSID '%s' (DA " MACSTR ")%s", @@ -709,6 +813,18 @@ void handle_probe_req(struct hostapd_data *hapd, /* TODO: verify that supp_rates contains at least one matching rate * with AP configuration */ + if (hapd->conf->no_probe_resp_if_seen_on && + is_multicast_ether_addr(mgmt->da) && + is_multicast_ether_addr(mgmt->bssid) && + sta_track_seen_on(hapd->iface, mgmt->sa, + hapd->conf->no_probe_resp_if_seen_on)) { + wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR + " since STA has been seen on %s", + hapd->conf->iface, MAC2STR(mgmt->sa), + hapd->conf->no_probe_resp_if_seen_on); + return; + } + #ifdef CONFIG_TESTING_OPTIONS if (hapd->iconf->ignore_probe_probability > 0.0 && drand48() < hapd->iconf->ignore_probe_probability) { @@ -719,7 +835,7 @@ void handle_probe_req(struct hostapd_data *hapd, } #endif /* CONFIG_TESTING_OPTIONS */ - resp = hostapd_gen_probe_resp(hapd, sta, mgmt, elems.p2p != NULL, + resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL, &resp_len); if (resp == NULL) return; @@ -774,7 +890,7 @@ static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd, "this"); /* Generate a Probe Response template for the non-P2P case */ - return hostapd_gen_probe_resp(hapd, NULL, NULL, 0, resp_len); + return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len); } #endif /* NEED_AP_MLME */ @@ -804,6 +920,10 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, if (hapd->p2p_beacon_ie) tail_len += wpabuf_len(hapd->p2p_beacon_ie); #endif /* CONFIG_P2P */ +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) + tail_len += wpabuf_len(hapd->iface->fst_ies); +#endif /* CONFIG_FST */ if (hapd->conf->vendor_elements) tail_len += wpabuf_len(hapd->conf->vendor_elements); @@ -833,7 +953,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, host_to_le16(hapd->iconf->beacon_int); /* hardware or low-level driver will setup seq_ctrl and timestamp */ - capab_info = hostapd_own_capab_info(hapd, NULL, 0); + capab_info = hostapd_own_capab_info(hapd); head->u.beacon.capab_info = host_to_le16(capab_info); pos = &head->u.beacon.variable[0]; @@ -902,6 +1022,15 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tailpos = hostapd_eid_roaming_consortium(hapd, tailpos); tailpos = hostapd_add_csa_elems(hapd, tailpos, tail, &hapd->cs_c_off_beacon); + +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) { + os_memcpy(tailpos, wpabuf_head(hapd->iface->fst_ies), + wpabuf_len(hapd->iface->fst_ies)); + tailpos += wpabuf_len(hapd->iface->fst_ies); + } +#endif /* CONFIG_FST */ + #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); @@ -963,8 +1092,14 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, params->basic_rates = hapd->iface->basic_rates; params->ssid = hapd->conf->ssid.ssid; params->ssid_len = hapd->conf->ssid.ssid_len; - params->pairwise_ciphers = hapd->conf->wpa_pairwise | - hapd->conf->rsn_pairwise; + if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) == + (WPA_PROTO_WPA | WPA_PROTO_RSN)) + params->pairwise_ciphers = hapd->conf->wpa_pairwise | + hapd->conf->rsn_pairwise; + else if (hapd->conf->wpa & WPA_PROTO_RSN) + params->pairwise_ciphers = hapd->conf->rsn_pairwise; + else if (hapd->conf->wpa & WPA_PROTO_WPA) + params->pairwise_ciphers = hapd->conf->wpa_pairwise; params->group_cipher = hapd->conf->wpa_group; params->key_mgmt_suites = hapd->conf->wpa_key_mgmt; params->auth_algs = hapd->conf->auth_algs; diff --git a/src/ap/beacon.h b/src/ap/beacon.h index 722159a7500c6..d98f42e8157ae 100644 --- a/src/ap/beacon.h +++ b/src/ap/beacon.h @@ -21,5 +21,10 @@ 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_expire(struct hostapd_iface *iface, int force); +struct hostapd_data * +sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr, + const char *ifname); #endif /* BEACON_H */ diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index 41ab988277bbd..c98978f33d05e 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -12,6 +12,7 @@ #include "common/ieee802_11_defs.h" #include "common/sae.h" #include "eapol_auth/eapol_auth_sm.h" +#include "fst/fst_ctrl_iface.h" #include "hostapd.h" #include "ieee802_1x.h" #include "wpa_auth.h" @@ -153,6 +154,13 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, } #endif /* CONFIG_SAE */ + if (sta->vlan_id > 0) { + res = os_snprintf(buf + len, buflen - len, "vlan_id=%d\n", + sta->vlan_id); + if (!os_snprintf_error(buflen - len, res)) + len += res; + } + return len; } @@ -199,7 +207,10 @@ int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr, return -1; } - return hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen); + ret = hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen); + ret += fst_ctrl_iface_mb_info(addr, buf + ret, buflen - ret); + + return ret; } diff --git a/src/ap/dfs.c b/src/ap/dfs.c index da6fd46467101..715f19b6ac7bd 100644 --- a/src/ap/dfs.c +++ b/src/ap/dfs.c @@ -122,6 +122,20 @@ static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans) } +static struct hostapd_channel_data * +dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx) +{ + int i; + + for (i = first_chan_idx; i < mode->num_channels; i++) { + if (mode->channels[i].freq == freq) + return &mode->channels[i]; + } + + return NULL; +} + + static int dfs_chan_range_available(struct hostapd_hw_modes *mode, int first_chan_idx, int num_chans, int skip_radar) @@ -129,15 +143,15 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode, struct hostapd_channel_data *first_chan, *chan; int i; - if (first_chan_idx + num_chans >= mode->num_channels) + if (first_chan_idx + num_chans > mode->num_channels) return 0; first_chan = &mode->channels[first_chan_idx]; for (i = 0; i < num_chans; i++) { - chan = &mode->channels[first_chan_idx + i]; - - if (first_chan->freq + i * 20 != chan->freq) + chan = dfs_get_chan_data(mode, first_chan->freq + i * 20, + first_chan_idx); + if (!chan) return 0; if (!dfs_channel_available(chan, skip_radar)) @@ -151,16 +165,10 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode, static int is_in_chanlist(struct hostapd_iface *iface, struct hostapd_channel_data *chan) { - int *entry; - - if (!iface->conf->chanlist) + if (!iface->conf->acs_ch_list.num) return 1; - for (entry = iface->conf->chanlist; *entry != -1; entry++) { - if (*entry == chan->chan) - return 1; - } - return 0; + return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan); } diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index a0adc67db430b..ca8b75c839060 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -18,6 +18,7 @@ #include "crypto/random.h" #include "p2p/p2p.h" #include "wps/wps.h" +#include "fst/fst.h" #include "wnm_ap.h" #include "hostapd.h" #include "ieee802_11.h" @@ -42,10 +43,10 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, struct ieee802_11_elems elems; const u8 *ie; size_t ielen; -#ifdef CONFIG_IEEE80211R +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; u8 *p = buf; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ u16 reason = WLAN_REASON_UNSPECIFIED; u16 status = WLAN_STATUS_SUCCESS; const u8 *p2p_dev_addr = NULL; @@ -58,8 +59,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, * running, so better make sure we stop processing such an * event here. */ - wpa_printf(MSG_DEBUG, "hostapd_notif_assoc: Skip event with " - "no address"); + wpa_printf(MSG_DEBUG, + "hostapd_notif_assoc: Skip event with no address"); return -1; } random_add_randomness(addr, ETH_ALEN); @@ -89,8 +90,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, } else { ie = NULL; ielen = 0; - wpa_printf(MSG_DEBUG, "STA did not include WPS/RSN/WPA IE in " - "(Re)AssocReq"); + wpa_printf(MSG_DEBUG, + "STA did not include WPS/RSN/WPA IE in (Re)AssocReq"); } sta = ap_get_sta(hapd, addr); @@ -126,8 +127,6 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, #ifdef CONFIG_IEEE80211N #ifdef NEED_AP_MLME if (elems.ht_capabilities && - elems.ht_capabilities_len >= - sizeof(struct ieee80211_ht_capabilities) && (hapd->iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { struct ieee80211_ht_capabilities *ht_cap = @@ -157,13 +156,20 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta->hs20_ie = NULL; #endif /* CONFIG_HS20 */ +#ifdef CONFIG_FST + wpabuf_free(sta->mb_ies); + if (hapd->iface->fst) + sta->mb_ies = mb_ies_by_info(&elems.mb_ies); + else + sta->mb_ies = NULL; +#endif /* CONFIG_FST */ + if (hapd->conf->wpa) { if (ie == NULL || ielen == 0) { #ifdef CONFIG_WPS if (hapd->conf->wps_state) { - wpa_printf(MSG_DEBUG, "STA did not include " - "WPA/RSN IE in (Re)Association " - "Request - possible WPS use"); + wpa_printf(MSG_DEBUG, + "STA did not include WPA/RSN IE in (Re)Association Request - possible WPS use"); sta->flags |= WLAN_STA_MAYBE_WPS; goto skip_wpa_check; } @@ -176,13 +182,14 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 && os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) { struct wpabuf *wps; + sta->flags |= WLAN_STA_WPS; wps = ieee802_11_vendor_ie_concat(ie, ielen, WPS_IE_VENDOR_TYPE); if (wps) { if (wps_is_20(wps)) { - wpa_printf(MSG_DEBUG, "WPS: STA " - "supports WPS 2.0"); + wpa_printf(MSG_DEBUG, + "WPS: STA supports WPS 2.0"); sta->flags |= WLAN_STA_WPS2; } wpabuf_free(wps); @@ -196,16 +203,17 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta->addr, p2p_dev_addr); if (sta->wpa_sm == NULL) { - wpa_printf(MSG_ERROR, "Failed to initialize WPA state " - "machine"); + wpa_printf(MSG_ERROR, + "Failed to initialize WPA state machine"); return -1; } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, ie, ielen, elems.mdie, elems.mdie_len); if (res != WPA_IE_OK) { - wpa_printf(MSG_DEBUG, "WPA/RSN information element " - "rejected? (res %u)", res); + wpa_printf(MSG_DEBUG, + "WPA/RSN information element rejected? (res %u)", + res); wpa_hexdump(MSG_DEBUG, "IE", ie, ielen); if (res == WPA_INVALID_GROUP) { reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID; @@ -248,14 +256,12 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, if (sta->sa_query_count == 0) ap_sta_start_sa_query(hapd, sta); -#ifdef CONFIG_IEEE80211R status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; p = hostapd_eid_assoc_comeback_time(hapd, sta, p); hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); -#endif /* CONFIG_IEEE80211R */ return 0; } @@ -283,6 +289,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, } else if (hapd->conf->wps_state) { #ifdef CONFIG_WPS struct wpabuf *wps; + if (req_ies) wps = ieee802_11_vendor_ie_concat(req_ies, req_ies_len, WPS_IE_VENDOR_TYPE); @@ -299,8 +306,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, if (wps) { sta->flags |= WLAN_STA_WPS; if (wps_is_20(wps)) { - wpa_printf(MSG_DEBUG, "WPS: STA supports " - "WPS 2.0"); + wpa_printf(MSG_DEBUG, + "WPS: STA supports WPS 2.0"); sta->flags |= WLAN_STA_WPS2; } } else @@ -322,8 +329,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (sta->wpa_sm == NULL) { - wpa_printf(MSG_WARNING, "Failed to initialize WPA " - "state machine"); + wpa_printf(MSG_WARNING, + "Failed to initialize WPA state machine"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm, @@ -395,8 +402,8 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) * was running, so better make sure we stop processing such an * event here. */ - wpa_printf(MSG_DEBUG, "hostapd_notif_disassoc: Skip event " - "with no address"); + wpa_printf(MSG_DEBUG, + "hostapd_notif_disassoc: Skip event with no address"); return; } @@ -405,8 +412,9 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) sta = ap_get_sta(hapd, addr); if (sta == NULL) { - wpa_printf(MSG_DEBUG, "Disassociation notification for " - "unknown STA " MACSTR, MAC2STR(addr)); + wpa_printf(MSG_DEBUG, + "Disassociation notification for unknown STA " + MACSTR, MAC2STR(addr)); return; } @@ -427,8 +435,8 @@ void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr) return; hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "disconnected due to excessive " - "missing ACKs"); + 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); @@ -452,8 +460,8 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, channel = hostapd_hw_get_channel(hapd, freq); if (!channel) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, "driver switched to " - "bad channel!"); + HOSTAPD_LEVEL_WARNING, + "driver switched to bad channel!"); return; } @@ -532,10 +540,9 @@ void hostapd_event_connect_failed_reason(struct hostapd_data *hapd, #ifdef CONFIG_ACS static void hostapd_acs_channel_selected(struct hostapd_data *hapd, - u8 pri_channel, u8 sec_channel) + struct acs_selected_channels *acs_res) { - int channel; - int ret; + int ret, i; if (hapd->iconf->channel) { wpa_printf(MSG_INFO, "ACS: Channel was already set to %d", @@ -543,29 +550,73 @@ static void hostapd_acs_channel_selected(struct hostapd_data *hapd, return; } - hapd->iface->freq = hostapd_hw_get_freq(hapd, pri_channel); + if (!hapd->iface->current_mode) { + for (i = 0; i < hapd->iface->num_hw_features; i++) { + struct hostapd_hw_modes *mode = + &hapd->iface->hw_features[i]; - channel = pri_channel; - if (!channel) { + if (mode->mode == acs_res->hw_mode) { + hapd->iface->current_mode = mode; + break; + } + } + if (!hapd->iface->current_mode) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "driver selected to bad hw_mode"); + return; + } + } + + hapd->iface->freq = hostapd_hw_get_freq(hapd, acs_res->pri_channel); + + if (!acs_res->pri_channel) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "driver switched to bad channel"); return; } - hapd->iconf->channel = channel; + hapd->iconf->channel = acs_res->pri_channel; + hapd->iconf->acs = 1; - if (sec_channel == 0) + if (acs_res->sec_channel == 0) hapd->iconf->secondary_channel = 0; - else if (sec_channel < pri_channel) + else if (acs_res->sec_channel < acs_res->pri_channel) hapd->iconf->secondary_channel = -1; - else if (sec_channel > pri_channel) + else if (acs_res->sec_channel > acs_res->pri_channel) hapd->iconf->secondary_channel = 1; else { wpa_printf(MSG_ERROR, "Invalid secondary channel!"); return; } + if (hapd->iface->conf->ieee80211ac) { + /* set defaults for backwards compatibility */ + hapd->iconf->vht_oper_centr_freq_seg1_idx = 0; + hapd->iconf->vht_oper_centr_freq_seg0_idx = 0; + hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; + if (acs_res->ch_width == 80) { + hapd->iconf->vht_oper_centr_freq_seg0_idx = + acs_res->vht_seg0_center_ch; + hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ; + } else if (acs_res->ch_width == 160) { + if (acs_res->vht_seg1_center_ch == 0) { + hapd->iconf->vht_oper_centr_freq_seg0_idx = + acs_res->vht_seg0_center_ch; + hapd->iconf->vht_oper_chwidth = + VHT_CHANWIDTH_160MHZ; + } else { + hapd->iconf->vht_oper_centr_freq_seg0_idx = + acs_res->vht_seg0_center_ch; + hapd->iconf->vht_oper_centr_freq_seg1_idx = + acs_res->vht_seg1_center_ch; + hapd->iconf->vht_oper_chwidth = + VHT_CHANWIDTH_80P80MHZ; + } + } + } + ret = hostapd_acs_completed(hapd->iface, 0); if (ret) { wpa_printf(MSG_ERROR, @@ -647,8 +698,8 @@ static void hostapd_notif_auth(struct hostapd_data *hapd, sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (sta->wpa_sm == NULL) { - wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA " - "state machine"); + wpa_printf(MSG_DEBUG, + "FT: Failed to initialize WPA state machine"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } @@ -683,7 +734,7 @@ static void hostapd_action_rx(struct hostapd_data *hapd, 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", + wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d", mgmt->u.action.category, (int) plen); sta = ap_get_sta(hapd, mgmt->sa); @@ -694,6 +745,7 @@ static void hostapd_action_rx(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211R 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 */ @@ -710,6 +762,13 @@ static void hostapd_action_rx(struct hostapd_data *hapd, ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len); } #endif /* CONFIG_WNM */ +#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 */ + } @@ -761,6 +820,7 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) if (hapd->ext_mgmt_frame_handling) { size_t hex_len = 2 * rx_mgmt->frame_len + 1; char *hex = os_malloc(hex_len); + if (hex) { wpa_snprintf_hex(hex, hex_len, rx_mgmt->frame, rx_mgmt->frame_len); @@ -778,8 +838,7 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) hapd = get_hapd_bssid(iface, bssid); if (hapd == NULL) { - u16 fc; - fc = le_to_host16(hdr->frame_control); + u16 fc = le_to_host16(hdr->frame_control); /* * Drop frames to unknown BSSIDs except for Beacon frames which @@ -798,6 +857,7 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) if (hapd == HAPD_BROADCAST) { size_t i; + ret = 0; for (i = 0; i < iface->num_bss; i++) { /* if bss is set, driver will call this function for @@ -824,6 +884,7 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, u16 stype, int ok) { struct ieee80211_hdr *hdr; + hdr = (struct ieee80211_hdr *) buf; hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len)); if (hapd == NULL || hapd == HAPD_BROADCAST) @@ -837,6 +898,7 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf, static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta = ap_get_sta(hapd, addr); + if (sta) return 0; @@ -863,11 +925,10 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src, size_t j; for (j = 0; j < iface->num_bss; j++) { - if ((sta = ap_get_sta(iface->bss[j], src))) { - if (sta->flags & WLAN_STA_ASSOC) { - hapd = iface->bss[j]; - break; - } + sta = ap_get_sta(iface->bss[j], src); + if (sta && sta->flags & WLAN_STA_ASSOC) { + hapd = iface->bss[j]; + break; } } @@ -927,7 +988,8 @@ static void hostapd_single_channel_get_survey(struct hostapd_iface *iface, if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED) return; - wpa_printf(MSG_DEBUG, "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)", + wpa_printf(MSG_DEBUG, + "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)", survey->freq, (unsigned long int) survey->channel_time, (unsigned long int) survey->channel_time_busy); @@ -1061,6 +1123,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->rx_mgmt.frame_len >= 24) { const struct ieee80211_hdr *hdr; u16 fc; + hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame; fc = le_to_host16(hdr->frame_control); if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && @@ -1248,9 +1311,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; #ifdef CONFIG_ACS case EVENT_ACS_CHANNEL_SELECTED: - hostapd_acs_channel_selected( - hapd, data->acs_selected_channels.pri_channel, - data->acs_selected_channels.sec_channel); + hostapd_acs_channel_selected(hapd, + &data->acs_selected_channels); break; #endif /* CONFIG_ACS */ default: diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c index 559d77f9ef2b8..082d0f53175e1 100644 --- a/src/ap/eap_user_db.c +++ b/src/ap/eap_user_db.c @@ -138,8 +138,12 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, char id_str[256], cmd[300]; size_t i; - if (identity_len >= sizeof(id_str)) + if (identity_len >= sizeof(id_str)) { + wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d", + __func__, (int) identity_len, + (int) (sizeof(id_str))); return NULL; + } os_memcpy(id_str, identity, identity_len); id_str[identity_len] = '\0'; for (i = 0; i < identity_len; i++) { @@ -182,7 +186,9 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, wpa_printf(MSG_DEBUG, "DB: %s", cmd); if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) != SQLITE_OK) { - wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL operation"); + wpa_printf(MSG_DEBUG, + "DB: Failed to complete SQL operation: %s db: %s", + sqlite3_errmsg(db), hapd->conf->eap_user_sqlite); } else if (hapd->tmp_eap_user.next) user = &hapd->tmp_eap_user; @@ -192,8 +198,10 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, wpa_printf(MSG_DEBUG, "DB: %s", cmd); if (sqlite3_exec(db, cmd, get_wildcard_cb, &hapd->tmp_eap_user, NULL) != SQLITE_OK) { - wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL " - "operation"); + wpa_printf(MSG_DEBUG, + "DB: Failed to complete SQL operation: %s db: %s", + sqlite3_errmsg(db), + hapd->conf->eap_user_sqlite); } else if (hapd->tmp_eap_user.next) { user = &hapd->tmp_eap_user; os_free(user->identity); diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 3e4e16b4f396a..c09c17a446963 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -17,6 +17,7 @@ #include "eap_server/tncs.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" +#include "fst/fst.h" #include "hostapd.h" #include "authsrv.h" #include "sta_info.h" @@ -179,6 +180,7 @@ int hostapd_reload_config(struct hostapd_iface *iface) hapd = iface->bss[j]; hapd->iconf = newconf; hapd->iconf->channel = oldconf->channel; + hapd->iconf->acs = oldconf->acs; hapd->iconf->secondary_channel = oldconf->secondary_channel; hapd->iconf->ieee80211n = oldconf->ieee80211n; hapd->iconf->ieee80211ac = oldconf->ieee80211ac; @@ -259,6 +261,7 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) { os_free(hapd->probereq_cb); hapd->probereq_cb = NULL; + hapd->num_probereq_cb = 0; #ifdef CONFIG_P2P wpabuf_free(hapd->p2p_beacon_ie); @@ -353,6 +356,22 @@ static void hostapd_cleanup(struct hostapd_data *hapd) } +static void sta_track_deinit(struct hostapd_iface *iface) +{ + struct hostapd_sta_info *info; + + if (!iface->num_sta_seen) + return; + + while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info, + list))) { + dl_list_del(&info->list); + iface->num_sta_seen--; + os_free(info); + } +} + + static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) { wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); @@ -368,6 +387,7 @@ static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) os_free(iface->basic_rates); iface->basic_rates = NULL; ap_list_deinit(iface); + sta_track_deinit(iface); } @@ -861,7 +881,7 @@ hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr) static int hostapd_setup_bss(struct hostapd_data *hapd, int first) { struct hostapd_bss_config *conf = hapd->conf; - u8 ssid[HOSTAPD_MAX_SSID_LEN + 1]; + u8 ssid[SSID_MAX_LEN + 1]; int ssid_len, set_ssid; char force_ifname[IFNAMSIZ]; u8 if_addr[ETH_ALEN]; @@ -1363,6 +1383,132 @@ fail: } +#ifdef CONFIG_FST + +static const u8 * fst_hostapd_get_bssid_cb(void *ctx) +{ + struct hostapd_data *hapd = ctx; + + return hapd->own_addr; +} + + +static void fst_hostapd_get_channel_info_cb(void *ctx, + enum hostapd_hw_mode *hw_mode, + u8 *channel) +{ + struct hostapd_data *hapd = ctx; + + *hw_mode = ieee80211_freq_to_chan(hapd->iface->freq, channel); +} + + +static void fst_hostapd_set_ies_cb(void *ctx, const struct wpabuf *fst_ies) +{ + struct hostapd_data *hapd = ctx; + + if (hapd->iface->fst_ies != fst_ies) { + hapd->iface->fst_ies = fst_ies; + if (ieee802_11_set_beacon(hapd)) + wpa_printf(MSG_WARNING, "FST: Cannot set beacon"); + } +} + + +static int fst_hostapd_send_action_cb(void *ctx, const u8 *da, + struct wpabuf *buf) +{ + struct hostapd_data *hapd = ctx; + + return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, da, + wpabuf_head(buf), wpabuf_len(buf)); +} + + +static const struct wpabuf * fst_hostapd_get_mb_ie_cb(void *ctx, const u8 *addr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = ap_get_sta(hapd, addr); + + return sta ? sta->mb_ies : NULL; +} + + +static void fst_hostapd_update_mb_ie_cb(void *ctx, const u8 *addr, + const u8 *buf, size_t size) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = ap_get_sta(hapd, addr); + + if (sta) { + struct mb_ies_info info; + + if (!mb_ies_info_by_ies(&info, buf, size)) { + wpabuf_free(sta->mb_ies); + sta->mb_ies = mb_ies_by_info(&info); + } + } +} + + +static const u8 * fst_hostapd_get_sta(struct fst_get_peer_ctx **get_ctx, + Boolean mb_only) +{ + struct sta_info *s = (struct sta_info *) *get_ctx; + + if (mb_only) { + for (; s && !s->mb_ies; s = s->next) + ; + } + + if (s) { + *get_ctx = (struct fst_get_peer_ctx *) s->next; + + return s->addr; + } + + *get_ctx = NULL; + return NULL; +} + + +static const u8 * fst_hostapd_get_peer_first(void *ctx, + struct fst_get_peer_ctx **get_ctx, + Boolean mb_only) +{ + struct hostapd_data *hapd = ctx; + + *get_ctx = (struct fst_get_peer_ctx *) hapd->sta_list; + + return fst_hostapd_get_sta(get_ctx, mb_only); +} + + +static const u8 * fst_hostapd_get_peer_next(void *ctx, + struct fst_get_peer_ctx **get_ctx, + Boolean mb_only) +{ + return fst_hostapd_get_sta(get_ctx, mb_only); +} + + +void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd, + struct fst_wpa_obj *iface_obj) +{ + iface_obj->ctx = hapd; + iface_obj->get_bssid = fst_hostapd_get_bssid_cb; + iface_obj->get_channel_info = fst_hostapd_get_channel_info_cb; + iface_obj->set_ies = fst_hostapd_set_ies_cb; + iface_obj->send_action = fst_hostapd_send_action_cb; + iface_obj->get_mb_ie = fst_hostapd_get_mb_ie_cb; + iface_obj->update_mb_ie = fst_hostapd_update_mb_ie_cb; + iface_obj->get_peer_first = fst_hostapd_get_peer_first; + iface_obj->get_peer_next = fst_hostapd_get_peer_next; +} + +#endif /* CONFIG_FST */ + + /** * hostapd_setup_interface_complete - Complete interface setup * @@ -1495,6 +1641,7 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) hostapd_tx_queue_params(iface); ap_list_init(iface); + dl_list_init(&iface->sta_seen); hostapd_set_acl(hapd); @@ -1528,6 +1675,22 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) #ifdef NEED_AP_MLME dfs_offload: #endif /* NEED_AP_MLME */ + +#ifdef CONFIG_FST + if (hapd->iconf->fst_cfg.group_id[0]) { + struct fst_wpa_obj iface_obj; + + fst_hostapd_fill_iface_obj(hapd, &iface_obj); + iface->fst = fst_attach(hapd->conf->iface, hapd->own_addr, + &iface_obj, &hapd->iconf->fst_cfg); + if (!iface->fst) { + wpa_printf(MSG_ERROR, "Could not attach to FST %s", + hapd->iconf->fst_cfg.group_id); + goto fail; + } + } +#endif /* CONFIG_FST */ + hostapd_set_state(iface, HAPD_IFACE_ENABLED); wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED); if (hapd->setup_complete_cb) @@ -1544,6 +1707,12 @@ fail: wpa_printf(MSG_ERROR, "Interface initialization failed"); hostapd_set_state(iface, HAPD_IFACE_DISABLED); wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); +#ifdef CONFIG_FST + if (iface->fst) { + fst_detach(iface->fst); + iface->fst = NULL; + } +#endif /* CONFIG_FST */ if (iface->interfaces && iface->interfaces->terminate_on_error) eloop_terminate(); return -1; @@ -1643,6 +1812,13 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); iface->wait_channel_update = 0; +#ifdef CONFIG_FST + if (iface->fst) { + fst_detach(iface->fst); + iface->fst = NULL; + } +#endif /* CONFIG_FST */ + for (j = iface->num_bss - 1; j >= 0; j--) hostapd_bss_deinit(iface->bss[j]); } @@ -2029,7 +2205,7 @@ hostapd_iface_alloc(struct hapd_interfaces *interfaces) static struct hostapd_config * hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname, - const char *ctrl_iface) + const char *ctrl_iface, const char *driver) { struct hostapd_bss_config *bss; struct hostapd_config *conf; @@ -2042,6 +2218,21 @@ hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname, return NULL; } + if (driver) { + int j; + + for (j = 0; wpa_drivers[j]; j++) { + if (os_strcmp(driver, wpa_drivers[j]->name) == 0) { + conf->driver = wpa_drivers[j]; + goto skip; + } + } + + wpa_printf(MSG_ERROR, + "Invalid/unknown driver '%s' - registering the default driver", + driver); + } + conf->driver = wpa_drivers[0]; if (conf->driver == NULL) { wpa_printf(MSG_ERROR, "No driver wrappers registered!"); @@ -2049,6 +2240,7 @@ hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname, return NULL; } +skip: bss = conf->last_bss = conf->bss[0]; os_strlcpy(bss->iface, ifname, sizeof(bss->iface)); @@ -2209,8 +2401,14 @@ int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf) if (conf && conf->bss) os_strlcpy(conf->bss[0]->iface, buf, sizeof(conf->bss[0]->iface)); - } else - conf = hostapd_config_alloc(interfaces, buf, ptr); + } else { + char *driver = os_strchr(ptr, ' '); + + if (driver) + *driver++ = '\0'; + conf = hostapd_config_alloc(interfaces, buf, ptr, driver); + } + if (conf == NULL || conf->bss == NULL) { wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " "for configuration", __func__); @@ -2722,4 +2920,43 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface, hostapd_enable_iface(iface); } + +struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces, + const char *ifname) +{ + size_t i, j; + + for (i = 0; i < interfaces->count; i++) { + struct hostapd_iface *iface = interfaces->iface[i]; + + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_data *hapd = iface->bss[j]; + + if (os_strcmp(ifname, hapd->conf->iface) == 0) + return hapd; + } + } + + return NULL; +} + #endif /* NEED_AP_MLME */ + + +void hostapd_periodic_iface(struct hostapd_iface *iface) +{ + size_t i; + + ap_list_timer(iface); + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *hapd = iface->bss[i]; + + if (!hapd->started) + continue; + +#ifndef CONFIG_NO_RADIUS + hostapd_acl_expire(hapd); +#endif /* CONFIG_NO_RADIUS */ + } +} diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 75cc24edb665b..dcf51f00f78d3 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -41,6 +41,7 @@ struct hapd_interfaces { size_t count; int global_ctrl_sock; + struct wpa_ctrl_dst *global_ctrl_dst; char *global_iface_path; char *global_iface_name; #ifndef CONFIG_NATIVE_WINDOWS @@ -49,6 +50,9 @@ struct hapd_interfaces { struct hostapd_iface **iface; size_t terminate_on_error; +#ifndef CONFIG_NO_VLAN + struct dynamic_iface *vlan_priv; +#endif /* CONFIG_NO_VLAN */ }; enum hostapd_chan_status { @@ -265,6 +269,7 @@ struct hostapd_data { /** Key used for generating SAE anti-clogging tokens */ u8 sae_token_key[8]; struct os_reltime last_sae_token_key_update; + int dot11RSNASAERetransPeriod; /* msec */ #endif /* CONFIG_SAE */ #ifdef CONFIG_TESTING_OPTIONS @@ -276,6 +281,12 @@ struct hostapd_data { }; +struct hostapd_sta_info { + struct dl_list list; + u8 addr[ETH_ALEN]; + struct os_reltime last_seen; +}; + /** * struct hostapd_iface - hostapd per-interface data structure */ @@ -305,6 +316,10 @@ struct hostapd_iface { unsigned int wait_channel_update:1; unsigned int cac_started:1; +#ifdef CONFIG_FST + struct fst_iface *fst; + const struct wpabuf *fst_ies; +#endif /* CONFIG_FST */ /* * When set, indicates that the driver will handle the AP @@ -400,6 +415,9 @@ struct hostapd_iface { void (*scan_cb)(struct hostapd_iface *iface); int num_ht40_scan_tries; + + struct dl_list sta_seen; /* struct hostapd_sta_info */ + unsigned int num_sta_seen; }; /* hostapd.c */ @@ -437,6 +455,7 @@ void 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); /* utils.c */ int hostapd_register_probereq_cb(struct hostapd_data *hapd, @@ -464,4 +483,12 @@ const struct hostapd_eap_user * hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, size_t identity_len, int phase2); +struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces, + const char *ifname); + +#ifdef CONFIG_FST +void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd, + struct fst_wpa_obj *iface_obj); +#endif /* CONFIG_FST */ + #endif /* HOSTAPD_H */ diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 05431d32c205d..fc8786dc311ca 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -260,8 +260,14 @@ static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface, res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan); - if (res == 2) - ieee80211n_switch_pri_sec(iface); + if (res == 2) { + if (iface->conf->no_pri_sec_switch) { + wpa_printf(MSG_DEBUG, + "Cannot switch PRI/SEC channels due to local constraint"); + } else { + ieee80211n_switch_pri_sec(iface); + } + } return !!res; } @@ -347,8 +353,13 @@ static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface, sec_freq = pri_freq + 20; else sec_freq = pri_freq - 20; - affected_start = (pri_freq + sec_freq) / 2 - 25; - affected_end = (pri_freq + sec_freq) / 2 + 25; + /* + * Note: Need to find the PRI channel also in cases where the affected + * channel is the SEC channel of a 40 MHz BSS, so need to include the + * scanning coverage here to be 40 MHz from the center frequency. + */ + affected_start = (pri_freq + sec_freq) / 2 - 40; + affected_end = (pri_freq + sec_freq) / 2 + 40; wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", affected_start, affected_end); @@ -510,7 +521,11 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) return 0; } - if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && + /* + * Driver ACS chosen channel may not be HT40 due to internal driver + * restrictions. + */ + if (!iface->conf->acs && (conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { wpa_printf(MSG_ERROR, "Driver does not support configured " "HT capability [HT40*]"); @@ -717,6 +732,15 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface) int ret; if (!iface->conf->ieee80211n) return 0; + + if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211B && + iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G && + (iface->conf->ht_capab & HT_CAP_INFO_DSSS_CCK40MHZ)) { + wpa_printf(MSG_DEBUG, + "Disable HT capability [DSSS_CCK-40] on 5 GHz band"); + iface->conf->ht_capab &= ~HT_CAP_INFO_DSSS_CCK40MHZ; + } + if (!ieee80211n_supported_ht_capab(iface)) return -1; #ifdef CONFIG_IEEE80211AC @@ -740,6 +764,9 @@ static int hostapd_is_usable_chan(struct hostapd_iface *iface, int i; struct hostapd_channel_data *chan; + if (!iface->current_mode) + return 0; + for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; if (chan->chan != channel) @@ -801,6 +828,12 @@ hostapd_check_chans(struct hostapd_iface *iface) static void hostapd_notify_bad_chans(struct hostapd_iface *iface) { + if (!iface->current_mode) { + hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Hardware does not support configured mode"); + return; + } hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, @@ -891,14 +924,18 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface) } if (iface->current_mode == NULL) { - wpa_printf(MSG_ERROR, "Hardware does not support configured " - "mode"); - hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, - "Hardware does not support configured mode " - "(%d) (hw_mode in hostapd.conf)", - (int) iface->conf->hw_mode); - return -2; + if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) || + !(iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY)) + { + wpa_printf(MSG_ERROR, + "Hardware does not support configured mode"); + hostapd_logger(iface->bss[0], NULL, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Hardware does not support configured mode (%d) (hw_mode in hostapd.conf)", + (int) iface->conf->hw_mode); + return -2; + } } switch (hostapd_check_chans(iface)) { diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h index 0f67ab8e5f37f..ca7f22ba205bf 100644 --- a/src/ap/hw_features.h +++ b/src/ap/hw_features.h @@ -36,6 +36,11 @@ static inline int hostapd_get_hw_features(struct hostapd_iface *iface) return -1; } +static inline int hostapd_acs_completed(struct hostapd_iface *iface, int err) +{ + return -1; +} + static inline int hostapd_select_hw_mode(struct hostapd_iface *iface) { return -100; diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 89911b1fdd42f..7bb18c01d1a19 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -23,6 +23,7 @@ #include "radius/radius_client.h" #include "p2p/p2p.h" #include "wps/wps.h" +#include "fst/fst.h" #include "hostapd.h" #include "beacon.h" #include "ieee802_11_auth.h" @@ -38,6 +39,7 @@ #include "p2p_hostapd.h" #include "ap_drv_ops.h" #include "wnm_ap.h" +#include "hw_features.h" #include "ieee802_11.h" #include "dfs.h" @@ -132,8 +134,7 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) } -u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, - int probe) +u16 hostapd_own_capab_info(struct hostapd_data *hapd) { int capab = WLAN_CAPABILITY_ESS; int privacy; @@ -166,20 +167,6 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, privacy = 1; #endif /* CONFIG_HS20 */ - if (sta) { - int policy, def_klen; - if (probe && sta->ssid_probe) { - policy = sta->ssid_probe->security_policy; - def_klen = sta->ssid_probe->wep.default_len; - } else { - policy = sta->ssid->security_policy; - def_klen = sta->ssid->wep.default_len; - } - privacy = policy != SECURITY_PLAINTEXT; - if (policy == SECURITY_IEEE_802_1X && def_klen == 0) - privacy = 0; - } - if (privacy) capab |= WLAN_CAPABILITY_PRIVACY; @@ -206,6 +193,7 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, } +#ifndef CONFIG_NO_RC4 static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, u16 auth_transaction, const u8 *challenge, int iswep) @@ -259,6 +247,7 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, return 0; } +#endif /* CONFIG_NO_RC4 */ static void send_auth_reply(struct hostapd_data *hapd, @@ -328,7 +317,6 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, #ifdef CONFIG_SAE -#define dot11RSNASAERetransPeriod 40 /* msec */ #define dot11RSNASAESync 5 /* attempts */ @@ -511,12 +499,14 @@ static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data) switch (sta->sae->state) { case SAE_COMMITTED: ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0); - eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000, + eloop_register_timeout(0, + hapd->dot11RSNASAERetransPeriod * 1000, auth_sae_retransmit_timer, hapd, sta); break; case SAE_CONFIRMED: ret = auth_sae_send_confirm(hapd, sta, hapd->own_addr); - eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000, + eloop_register_timeout(0, + hapd->dot11RSNASAERetransPeriod * 1000, auth_sae_retransmit_timer, hapd, sta); break; default: @@ -542,7 +532,7 @@ static void sae_set_retransmit_timer(struct hostapd_data *hapd, return; eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta); - eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000, + eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000, auth_sae_retransmit_timer, hapd, sta); } @@ -624,7 +614,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_SUCCESS; sta->sae->sync++; - ret = auth_sae_send_commit(hapd, sta, bssid, 1); + ret = auth_sae_send_commit(hapd, sta, bssid, 0); if (ret) return ret; @@ -784,6 +774,12 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, ((const u8 *) mgmt) + len - mgmt->u.auth.variable, &token, &token_len, hapd->conf->sae_groups); + if (resp == SAE_SILENTLY_DISCARD) { + wpa_printf(MSG_DEBUG, + "SAE: Drop commit message from " MACSTR " due to reflection attack", + MAC2STR(sta->addr)); + return; + } if (token && check_sae_token(hapd, sta->addr, token, token_len) < 0) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message with " @@ -934,6 +930,16 @@ static void handle_auth(struct hostapd_data *hapd, challenge ? " challenge" : "", seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : ""); +#ifdef CONFIG_NO_RC4 + if (auth_alg == WLAN_AUTH_SHARED_KEY) { + wpa_printf(MSG_INFO, + "Unsupported authentication algorithm (%d)", + auth_alg); + resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + goto fail; + } +#endif /* CONFIG_NO_RC4 */ + if (hapd->tkip_countermeasures) { resp = WLAN_REASON_MICHAEL_MIC_FAILURE; goto fail; @@ -972,6 +978,61 @@ static void handle_auth(struct hostapd_data *hapd, goto fail; } + if (hapd->conf->no_auth_if_seen_on) { + struct hostapd_data *other; + + other = sta_track_seen_on(hapd->iface, mgmt->sa, + hapd->conf->no_auth_if_seen_on); + if (other) { + u8 *pos; + u32 info; + u8 op_class, channel, phytype; + + wpa_printf(MSG_DEBUG, "%s: Reject authentication from " + MACSTR " since STA has been seen on %s", + hapd->conf->iface, MAC2STR(mgmt->sa), + hapd->conf->no_auth_if_seen_on); + + resp = WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION; + pos = &resp_ies[0]; + *pos++ = WLAN_EID_NEIGHBOR_REPORT; + *pos++ = 13; + os_memcpy(pos, other->own_addr, ETH_ALEN); + pos += ETH_ALEN; + info = 0; /* TODO: BSSID Information */ + WPA_PUT_LE32(pos, info); + pos += 4; + if (other->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD) + phytype = 8; /* dmg */ + else if (other->iconf->ieee80211ac) + phytype = 9; /* vht */ + else if (other->iconf->ieee80211n) + phytype = 7; /* ht */ + else if (other->iconf->hw_mode == + HOSTAPD_MODE_IEEE80211A) + phytype = 4; /* ofdm */ + else if (other->iconf->hw_mode == + HOSTAPD_MODE_IEEE80211G) + phytype = 6; /* erp */ + else + phytype = 5; /* hrdsss */ + if (ieee80211_freq_to_channel_ext( + hostapd_hw_get_freq(other, + other->iconf->channel), + other->iconf->secondary_channel, + other->iconf->ieee80211ac, + &op_class, &channel) == NUM_HOSTAPD_MODES) { + op_class = 0; + channel = other->iconf->channel; + } + *pos++ = op_class; + *pos++ = channel; + *pos++ = phytype; + resp_ies_len = pos - &resp_ies[0]; + goto fail; + } + } + res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len, &session_timeout, &acct_interim_interval, &vlan_id, @@ -1081,6 +1142,7 @@ static void handle_auth(struct hostapd_data *hapd, sta->auth_alg = WLAN_AUTH_OPEN; mlme_authenticate_indication(hapd, sta); break; +#ifndef CONFIG_NO_RC4 case WLAN_AUTH_SHARED_KEY: resp = auth_shared_key(hapd, sta, auth_transaction, challenge, fc & WLAN_FC_ISWEP); @@ -1094,6 +1156,7 @@ static void handle_auth(struct hostapd_data *hapd, resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN; } break; +#endif /* CONFIG_NO_RC4 */ #ifdef CONFIG_IEEE80211R case WLAN_AUTH_FT: sta->auth_alg = WLAN_AUTH_FT; @@ -1297,8 +1360,7 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, if (resp != WLAN_STATUS_SUCCESS) return resp; #ifdef CONFIG_IEEE80211N - resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities, - elems.ht_capabilities_len); + resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities); if (resp != WLAN_STATUS_SUCCESS) return resp; if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && @@ -1311,14 +1373,15 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_IEEE80211AC - resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities, - elems.vht_capabilities_len); - if (resp != WLAN_STATUS_SUCCESS) - return resp; + if (hapd->iconf->ieee80211ac) { + resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities); + if (resp != WLAN_STATUS_SUCCESS) + return resp; - resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif); - if (resp != WLAN_STATUS_SUCCESS) - return resp; + resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + } if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && !(sta->flags & WLAN_STA_VHT)) { @@ -1546,6 +1609,14 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, sta->hs20_ie = NULL; #endif /* CONFIG_HS20 */ +#ifdef CONFIG_FST + wpabuf_free(sta->mb_ies); + if (hapd->iface->fst) + sta->mb_ies = mb_ies_by_info(&elems.mb_ies); + else + sta->mb_ies = NULL; +#endif /* CONFIG_FST */ + return WLAN_STATUS_SUCCESS; } @@ -1594,7 +1665,7 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, send_len = IEEE80211_HDRLEN; send_len += sizeof(reply->u.assoc_resp); reply->u.assoc_resp.capab_info = - host_to_le16(hostapd_own_capab_info(hapd, sta, 0)); + host_to_le16(hostapd_own_capab_info(hapd)); reply->u.assoc_resp.status_code = host_to_le16(status_code); reply->u.assoc_resp.aid = host_to_le16(sta->aid | BIT(14) | BIT(15)); /* Supported rates */ @@ -1634,6 +1705,14 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, if (sta->qos_map_enabled) p = hostapd_eid_qos_map_set(hapd, p); +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) { + os_memcpy(p, wpabuf_head(hapd->iface->fst_ies), + wpabuf_len(hapd->iface->fst_ies)); + p += wpabuf_len(hapd->iface->fst_ies); + } +#endif /* CONFIG_FST */ + #ifdef CONFIG_IEEE80211AC if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT)) p = hostapd_eid_vendor_vht(hapd, p); @@ -2112,10 +2191,20 @@ static int handle_action(struct hostapd_data *hapd, ieee802_11_rx_wnm_action_ap(hapd, mgmt, len); return 1; #endif /* CONFIG_WNM */ +#ifdef CONFIG_FST + case WLAN_ACTION_FST: + if (hapd->iface->fst) + fst_rx_action(hapd->iface->fst, mgmt, len); + else + wpa_printf(MSG_DEBUG, + "FST: Ignore FST Action frame - no FST attached"); + return 1; +#endif /* CONFIG_FST */ case WLAN_ACTION_PUBLIC: case WLAN_ACTION_PROTECTED_DUAL: #ifdef CONFIG_IEEE80211N - if (mgmt->u.action.u.public_action.action == + 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 " @@ -2248,6 +2337,9 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, return 0; } + if (hapd->iconf->track_sta_max_num) + sta_track_add(hapd->iface, mgmt->sa); + switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth"); @@ -2335,7 +2427,7 @@ static void hostapd_set_wds_encryption(struct hostapd_data *hapd, char *ifname_wds) { int i; - struct hostapd_ssid *ssid = sta->ssid; + struct hostapd_ssid *ssid = &hapd->conf->ssid; if (hapd->conf->ieee802_1x || hapd->conf->wpa) return; @@ -2473,11 +2565,11 @@ static void handle_assoc_cb(struct hostapd_data *hapd, * so bind it to the selected VLAN interface now, since the * interface selection is not going to change anymore. */ - if (ap_sta_bind_vlan(hapd, sta, 0) < 0) + if (ap_sta_bind_vlan(hapd, sta) < 0) return; } else if (sta->vlan_id) { /* VLAN ID already set (e.g., by PMKSA caching), so bind STA */ - if (ap_sta_bind_vlan(hapd, sta, 0) < 0) + if (ap_sta_bind_vlan(hapd, sta) < 0) return; } diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 41c27d906e72a..44c1bff364ac4 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -14,6 +14,7 @@ struct hostapd_data; struct sta_info; struct hostapd_frame_info; struct ieee80211_ht_capabilities; +struct ieee80211_vht_capabilities; struct ieee80211_mgmt; int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, @@ -40,8 +41,7 @@ static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd, return 0; } #endif /* NEED_AP_MLME */ -u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, - int probe); +u16 hostapd_own_capab_info(struct hostapd_data *hapd); void ap_ht2040_timeout(void *eloop_data, void *user_data); u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid); @@ -62,7 +62,7 @@ void hostapd_get_vht_capab(struct hostapd_data *hapd, struct ieee80211_vht_capabilities *vht_cap, struct ieee80211_vht_capabilities *neg_vht_cap); u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *ht_capab, size_t ht_capab_len); + const u8 *ht_capab); u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ie, size_t len); @@ -70,7 +70,7 @@ void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta); void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta); void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta); u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *vht_capab, size_t vht_capab_len); + const u8 *vht_capab); u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_opmode); void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c index 56c3ce0313d43..531a67da412c6 100644 --- a/src/ap/ieee802_11_auth.c +++ b/src/ap/ieee802_11_auth.c @@ -399,19 +399,15 @@ static void hostapd_acl_expire_queries(struct hostapd_data *hapd, /** * hostapd_acl_expire - ACL cache expiration callback - * @eloop_ctx: struct hostapd_data * - * @timeout_ctx: Not used + * @hapd: struct hostapd_data * */ -static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) +void hostapd_acl_expire(struct hostapd_data *hapd) { - struct hostapd_data *hapd = eloop_ctx; struct os_reltime now; os_get_reltime(&now); hostapd_acl_expire_cache(hapd, &now); hostapd_acl_expire_queries(hapd, &now); - - eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); } @@ -561,6 +557,19 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED && !cache->psk) cache->accepted = HOSTAPD_ACL_REJECT; + + if (cache->vlan_id && + !hostapd_vlan_id_valid(hapd->conf->vlan, cache->vlan_id)) { + hostapd_logger(hapd, query->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "Invalid VLAN ID %d received from RADIUS server", + cache->vlan_id); + cache->vlan_id = 0; + } + if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED && + !cache->vlan_id) + cache->accepted = HOSTAPD_ACL_REJECT; } else cache->accepted = HOSTAPD_ACL_REJECT; cache->next = hapd->acl_cache; @@ -602,8 +611,6 @@ int hostapd_acl_init(struct hostapd_data *hapd) if (radius_client_register(hapd->radius, RADIUS_AUTH, hostapd_acl_recv_radius, hapd)) return -1; - - eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); #endif /* CONFIG_NO_RADIUS */ return 0; @@ -619,8 +626,6 @@ void hostapd_acl_deinit(struct hostapd_data *hapd) struct hostapd_acl_query_data *query, *prev; #ifndef CONFIG_NO_RADIUS - eloop_cancel_timeout(hostapd_acl_expire, hapd, NULL); - hostapd_acl_cache_free(hapd->acl_cache); #endif /* CONFIG_NO_RADIUS */ diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h index 2bc1065a226c9..b66f244b3ebc4 100644 --- a/src/ap/ieee802_11_auth.h +++ b/src/ap/ieee802_11_auth.h @@ -24,5 +24,6 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, 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); +void hostapd_acl_expire(struct hostapd_data *hapd); #endif /* IEEE802_11_AUTH_H */ diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c index 4b0653de95dbd..11fde2a263946 100644 --- a/src/ap/ieee802_11_ht.c +++ b/src/ap/ieee802_11_ht.c @@ -209,7 +209,7 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, struct hostapd_iface *iface = hapd->iface; struct ieee80211_2040_bss_coex_ie *bc_ie; struct ieee80211_2040_intol_chan_report *ic_report; - int is_ht_allowed = 1; + int is_ht40_allowed = 1; int i; const u8 *start = (const u8 *) mgmt; const u8 *data = start + IEEE80211_HDRLEN + 2; @@ -242,7 +242,7 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "20 MHz BSS width request bit is set in BSS coexistence information field"); - is_ht_allowed = 0; + is_ht40_allowed = 0; } if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) { @@ -250,7 +250,7 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "40 MHz intolerant bit is set in BSS coexistence information field"); - is_ht_allowed = 0; + is_ht40_allowed = 0; } if (start + len - data >= 3 && @@ -276,13 +276,13 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, HOSTAPD_LEVEL_DEBUG, "20_40_INTOLERANT channel %d reported", chan); - is_ht_allowed = 0; + is_ht40_allowed = 0; } } - wpa_printf(MSG_DEBUG, "is_ht_allowed=%d num_sta_ht40_intolerant=%d", - is_ht_allowed, iface->num_sta_ht40_intolerant); + wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d", + is_ht40_allowed, iface->num_sta_ht40_intolerant); - if (!is_ht_allowed && + if (!is_ht40_allowed && (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { if (iface->conf->secondary_channel) { hostapd_logger(hapd, mgmt->sa, @@ -310,12 +310,15 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *ht_capab, size_t ht_capab_len) + const u8 *ht_capab) { - /* Disable HT caps for STAs associated to no-HT BSSes. */ + /* + * Disable HT caps for STAs associated to no-HT BSSes, or for stations + * that did not specify a valid WMM IE in the (Re)Association Request + * frame. + */ if (!ht_capab || - ht_capab_len < sizeof(struct ieee80211_ht_capabilities) || - hapd->conf->disable_11n) { + !(sta->flags & WLAN_STA_WMM) || 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_vht.c b/src/ap/ieee802_11_vht.c index 171538ad74a7a..5bf1b5d72002a 100644 --- a/src/ap/ieee802_11_vht.c +++ b/src/ap/ieee802_11_vht.c @@ -132,11 +132,10 @@ static int check_valid_vht_mcs(struct hostapd_hw_modes *mode, u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *vht_capab, size_t vht_capab_len) + const u8 *vht_capab) { /* Disable VHT caps for STAs associated to no-VHT BSSes. */ if (!vht_capab || - vht_capab_len < sizeof(struct ieee80211_vht_capabilities) || hapd->conf->disable_11ac || !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) { sta->flags &= ~WLAN_STA_VHT; diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index 79dc0f9574880..0f2d428cf752a 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -125,6 +125,9 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, } +#ifndef CONFIG_FIPS +#ifndef CONFIG_NO_RC4 + static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, struct sta_info *sta, int idx, int broadcast, @@ -204,7 +207,7 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, } -void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) +static void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) { struct eapol_authenticator *eapol = hapd->eapol_auth; struct eapol_state_machine *sm = sta->eapol_sm; @@ -259,6 +262,9 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) } } +#endif /* CONFIG_NO_RC4 */ +#endif /* CONFIG_FIPS */ + const char *radius_mode_txt(struct hostapd_data *hapd) { @@ -346,7 +352,8 @@ static int add_common_radius_sta_attr_rsn(struct hostapd_data *hapd, return -1; } - suite = wpa_cipher_to_suite((hapd->conf->wpa & 0x2) ? + suite = wpa_cipher_to_suite(((hapd->conf->wpa & 0x2) || + hapd->conf->osen) ? WPA_PROTO_RSN : WPA_PROTO_WPA, hapd->conf->wpa_group); if (!hostapd_config_get_radius_attr(req_attr, @@ -453,7 +460,7 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd, } #endif /* CONFIG_IEEE80211R */ - if (hapd->conf->wpa && sta->wpa_sm && + if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm && add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0) return -1; @@ -599,7 +606,7 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, goto fail; } - if (eap && !radius_msg_add_eap(msg, eap, len)) { + if (!radius_msg_add_eap(msg, eap, len)) { wpa_printf(MSG_INFO, "Could not add EAP-Message"); goto fail; } @@ -1108,8 +1115,6 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); if (pmksa) { - int old_vlanid; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "PMK from PMKSA cache - skip IEEE 802.1X/EAP"); @@ -1123,11 +1128,8 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) sta->eapol_sm->authFail = FALSE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); - old_vlanid = sta->vlan_id; pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm); - if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) - sta->vlan_id = 0; - ap_sta_bind_vlan(hapd, sta, old_vlanid); + ap_sta_bind_vlan(hapd, sta); } else { if (reassoc) { /* @@ -1290,7 +1292,7 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, struct sta_info *sta, struct radius_msg *msg) { - u8 *class; + u8 *attr_class; size_t class_len; struct eapol_state_machine *sm = sta->eapol_sm; int count, i; @@ -1312,12 +1314,12 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, nclass_count = 0; - class = NULL; + attr_class = NULL; for (i = 0; i < count; i++) { do { if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS, - &class, &class_len, - class) < 0) { + &attr_class, &class_len, + attr_class) < 0) { i = count; break; } @@ -1327,7 +1329,7 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, if (nclass[nclass_count].data == NULL) break; - os_memcpy(nclass[nclass_count].data, class, class_len); + os_memcpy(nclass[nclass_count].data, attr_class, class_len); nclass[nclass_count].len = class_len; nclass_count++; } @@ -1590,7 +1592,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, struct hostapd_data *hapd = data; struct sta_info *sta; u32 session_timeout = 0, termination_action, acct_interim_interval; - int session_timeout_set, old_vlanid = 0; + int session_timeout_set, vlan_id = 0; struct eapol_state_machine *sm; int override_eapReq = 0; struct radius_hdr *hdr = radius_msg_get_hdr(msg); @@ -1657,20 +1659,27 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, switch (hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: - if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) - sta->vlan_id = 0; + if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) + vlan_id = 0; #ifndef CONFIG_NO_VLAN - else { - old_vlanid = sta->vlan_id; - sta->vlan_id = radius_msg_get_vlanid(msg); - } - if (sta->vlan_id > 0 && - hostapd_vlan_id_valid(hapd->conf->vlan, sta->vlan_id)) { + else + vlan_id = radius_msg_get_vlanid(msg); + if (vlan_id > 0 && + hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, - "VLAN ID %d", sta->vlan_id); - } else if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_REQUIRED) { + "VLAN ID %d", vlan_id); + } else if (vlan_id > 0) { + sta->eapol_sm->authFail = TRUE; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "Invalid VLAN ID %d received from RADIUS server", + vlan_id); + break; + } else if (hapd->conf->ssid.dynamic_vlan == + DYNAMIC_VLAN_REQUIRED) { sta->eapol_sm->authFail = TRUE; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, @@ -1681,7 +1690,9 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, } #endif /* CONFIG_NO_VLAN */ - if (ap_sta_bind_vlan(hapd, sta, old_vlanid) < 0) + sta->vlan_id = vlan_id; + if ((sta->flags & WLAN_STA_ASSOC) && + ap_sta_bind_vlan(hapd, sta) < 0) break; sta->session_timeout_set = !!session_timeout_set; @@ -1926,10 +1937,11 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, struct hostapd_data *hapd = ctx; const struct hostapd_eap_user *eap_user; int i; + int rv = -1; eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2); if (eap_user == NULL) - return -1; + goto out; os_memset(user, 0, sizeof(*user)); user->phase2 = phase2; @@ -1941,7 +1953,7 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, if (eap_user->password) { user->password = os_malloc(eap_user->password_len); if (user->password == NULL) - return -1; + goto out; os_memcpy(user->password, eap_user->password, eap_user->password_len); user->password_len = eap_user->password_len; @@ -1951,8 +1963,13 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, user->macacl = eap_user->macacl; user->ttls_auth = eap_user->ttls_auth; user->remediation = eap_user->remediation; + rv = 0; - return 0; +out: + if (rv) + wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__); + + return rv; } @@ -2012,9 +2029,13 @@ static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx) static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx) { +#ifndef CONFIG_FIPS +#ifndef CONFIG_NO_RC4 struct hostapd_data *hapd = ctx; struct sta_info *sta = sta_ctx; ieee802_1x_tx_key(hapd, sta); +#endif /* CONFIG_NO_RC4 */ +#endif /* CONFIG_FIPS */ } @@ -2085,6 +2106,7 @@ int ieee802_1x_init(struct hostapd_data *hapd) conf.erp_send_reauth_start = hapd->conf->erp_send_reauth_start; conf.erp_domain = hapd->conf->erp_domain; conf.erp = hapd->conf->eap_server_erp; + conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; 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; @@ -2332,9 +2354,9 @@ void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth) } -static const char * bool_txt(Boolean bool) +static const char * bool_txt(Boolean val) { - return bool ? "TRUE" : "FALSE"; + return val ? "TRUE" : "FALSE"; } diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h index de6e0e75fac3b..14d69556993c4 100644 --- a/src/ap/ieee802_1x.h +++ b/src/ap/ieee802_1x.h @@ -23,7 +23,6 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_free_station(struct sta_info *sta); -void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized); diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c index b0d42dcd82c49..4a87721e2ecfc 100644 --- a/src/ap/ndisc_snoop.c +++ b/src/ap/ndisc_snoop.c @@ -81,12 +81,24 @@ static int sta_has_ip6addr(struct sta_info *sta, struct in6_addr *addr) } +static void ucast_to_stas(struct hostapd_data *hapd, const u8 *buf, size_t len) +{ + struct sta_info *sta; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (!(sta->flags & WLAN_STA_AUTHORIZED)) + continue; + x_snoop_mcast_to_ucast_convert_send(hapd, sta, (u8 *) buf, len); + } +} + + static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct hostapd_data *hapd = ctx; struct icmpv6_ndmsg *msg; - struct in6_addr *saddr; + struct in6_addr saddr; struct sta_info *sta; int res; char addrtxt[INET6_ADDRSTRLEN + 1]; @@ -101,25 +113,30 @@ static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf, if (msg->opt_type != SOURCE_LL_ADDR) return; - saddr = &msg->ipv6h.ip6_src; - if (!(saddr->s6_addr32[0] == 0 && saddr->s6_addr32[1] == 0 && - saddr->s6_addr32[2] == 0 && saddr->s6_addr32[3] == 0)) { + /* + * IPv6 header may not be 32-bit aligned in the buffer, so use + * a local copy to avoid unaligned reads. + */ + os_memcpy(&saddr, &msg->ipv6h.ip6_src, sizeof(saddr)); + if (!(saddr.s6_addr32[0] == 0 && saddr.s6_addr32[1] == 0 && + saddr.s6_addr32[2] == 0 && saddr.s6_addr32[3] == 0)) { if (len < ETH_HLEN + sizeof(*msg) + ETH_ALEN) return; sta = ap_get_sta(hapd, msg->opt_lladdr); if (!sta) return; - if (sta_has_ip6addr(sta, saddr)) + if (sta_has_ip6addr(sta, &saddr)) return; - if (inet_ntop(AF_INET6, saddr, addrtxt, sizeof(addrtxt)) - == NULL) + if (inet_ntop(AF_INET6, &saddr, addrtxt, + sizeof(addrtxt)) == NULL) addrtxt[0] = '\0'; wpa_printf(MSG_DEBUG, "ndisc_snoop: Learned new IPv6 address %s for " MACSTR, addrtxt, MAC2STR(sta->addr)); - hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) saddr); - res = hostapd_drv_br_add_ip_neigh(hapd, 6, (u8 *) saddr, + hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &saddr); + res = hostapd_drv_br_add_ip_neigh(hapd, 6, + (u8 *) &saddr, 128, sta->addr); if (res) { wpa_printf(MSG_ERROR, @@ -128,21 +145,17 @@ static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf, return; } - if (sta_ip6addr_add(sta, saddr)) + if (sta_ip6addr_add(sta, &saddr)) return; } break; case ROUTER_ADVERTISEMENT: - if (!hapd->conf->disable_dgaf) - return; - /* fall through */ + if (hapd->conf->disable_dgaf) + ucast_to_stas(hapd, buf, len); + break; case NEIGHBOR_ADVERTISEMENT: - for (sta = hapd->sta_list; sta; sta = sta->next) { - if (!(sta->flags & WLAN_STA_AUTHORIZED)) - continue; - x_snoop_mcast_to_ucast_convert_send(hapd, sta, - (u8 *) buf, len); - } + if (hapd->conf->na_mcast_to_ucast) + ucast_to_stas(hapd, buf, len); break; default: break; diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 7e75e1a61e1ae..d64307ccfd085 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -16,6 +16,7 @@ #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" +#include "fst/fst.h" #include "hostapd.h" #include "accounting.h" #include "ieee802_1x.h" @@ -171,6 +172,19 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) !(sta->flags & WLAN_STA_PREAUTH)) hostapd_drv_sta_remove(hapd, sta->addr); +#ifndef CONFIG_NO_VLAN + if (sta->vlan_id_bound) { + /* + * Need to remove the STA entry before potentially removing the + * VLAN. + */ + if (hapd->iface->driver_ap_teardown && + !(sta->flags & WLAN_STA_PREAUTH)) + hostapd_drv_sta_remove(hapd, sta->addr); + vlan_remove_dynamic(hapd, sta->vlan_id_bound); + } +#endif /* CONFIG_NO_VLAN */ + ap_sta_hash_del(hapd, sta); ap_sta_list_del(hapd, sta); @@ -283,6 +297,9 @@ 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); +#ifdef CONFIG_FST + wpabuf_free(sta->mb_ies); +#endif /* CONFIG_FST */ os_free(sta->ht_capabilities); os_free(sta->vht_capabilities); @@ -619,7 +636,6 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) hapd->sta_list = sta; hapd->num_sta++; ap_sta_hash_add(hapd, sta); - sta->ssid = &hapd->conf->ssid; ap_sta_remove_in_other_bss(hapd, sta); sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; dl_list_init(&sta->ip6addr); @@ -768,26 +784,19 @@ int ap_sta_wps_cancel(struct hostapd_data *hapd, #endif /* CONFIG_WPS */ -int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, - int old_vlanid) +int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) { #ifndef CONFIG_NO_VLAN const char *iface; struct hostapd_vlan *vlan = NULL; int ret; - - /* - * Do not proceed furthur if the vlan id remains same. We do not want - * duplicate dynamic vlan entries. - */ - if (sta->vlan_id == old_vlanid) - return 0; + int old_vlanid = sta->vlan_id_bound; iface = hapd->conf->iface; - if (sta->ssid->vlan[0]) - iface = sta->ssid->vlan; + if (hapd->conf->ssid.vlan[0]) + iface = hapd->conf->ssid.vlan; - if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) + if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) sta->vlan_id = 0; else if (sta->vlan_id > 0) { struct hostapd_vlan *wildcard_vlan = NULL; @@ -805,6 +814,14 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, iface = vlan->ifname; } + /* + * Do not increment ref counters if the VLAN ID remains same, but do + * not skip hostapd_drv_set_sta_vlan() as hostapd_drv_sta_remove() might + * have been called before. + */ + if (sta->vlan_id == old_vlanid) + goto skip_counting; + if (sta->vlan_id > 0 && vlan == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not find VLAN for " @@ -825,7 +842,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, } iface = vlan->ifname; - if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) { + if (vlan_setup_encryption_dyn(hapd, iface) != 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not " @@ -838,7 +855,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN " "interface '%s'", iface); } else if (vlan && vlan->vlan_id == sta->vlan_id) { - if (sta->vlan_id > 0) { + if (vlan->dynamic_vlan > 0) { vlan->dynamic_vlan++; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -852,7 +869,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, * configuration for the case where hostapd did not yet know * which keys are to be used when the interface was added. */ - if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) { + if (vlan_setup_encryption_dyn(hapd, iface) != 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not " @@ -862,6 +879,10 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, } } + /* ref counters have been increased, so mark the station */ + sta->vlan_id_bound = sta->vlan_id; + +skip_counting: hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "binding station to interface " "'%s'", iface); @@ -876,10 +897,10 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, "entry to vlan_id=%d", sta->vlan_id); } -done: /* During 1x reauth, if the vlan id changes, then remove the old id. */ - if (old_vlanid > 0) + if (old_vlanid > 0 && old_vlanid != sta->vlan_id) vlan_remove_dynamic(hapd, old_vlanid); +done: return ret; #else /* CONFIG_NO_VLAN */ @@ -1043,6 +1064,16 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO, AP_STA_DISCONNECTED "%s", buf); } + +#ifdef CONFIG_FST + if (hapd->iface->fst) { + if (authorized) + fst_notify_peer_connected(hapd->iface->fst, sta->addr); + else + fst_notify_peer_disconnected(hapd->iface->fst, + sta->addr); + } +#endif /* CONFIG_FST */ } diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 57551ab17d5dd..420d64e5793bf 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -117,10 +117,8 @@ struct sta_info { struct wpa_state_machine *wpa_sm; struct rsn_preauth_interface *preauth_iface; - struct hostapd_ssid *ssid; /* SSID selection based on (Re)AssocReq */ - struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */ - - int vlan_id; + int vlan_id; /* 0: none, >0: VID */ + int vlan_id_bound; /* updated by ap_sta_bind_vlan() */ /* PSKs from RADIUS authentication server */ struct hostapd_sta_wpa_psk_short *psk; @@ -155,6 +153,9 @@ struct sta_info { struct wpabuf *hs20_deauth_req; char *hs20_session_info_url; int hs20_disassoc_timer; +#ifdef CONFIG_FST + struct wpabuf *mb_ies; /* MB IEs from (Re)Association Request */ +#endif /* CONFIG_FST */ struct os_reltime connected_time; @@ -218,8 +219,7 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, int ap_sta_wps_cancel(struct hostapd_data *hapd, struct sta_info *sta, void *ctx); #endif /* CONFIG_WPS */ -int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, - int old_vlanid); +int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta); void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta); void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta); int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta); diff --git a/src/ap/utils.c b/src/ap/utils.c index 931968c84b0e8..fcb371bec2834 100644 --- a/src/ap/utils.c +++ b/src/ap/utils.c @@ -10,6 +10,7 @@ #include "common.h" #include "common/ieee802_11_defs.h" +#include "fst/fst.h" #include "sta_info.h" #include "hostapd.h" @@ -55,10 +56,20 @@ static int prune_associations(struct hostapd_iface *iface, void *ctx) ohapd = iface->bss[j]; if (ohapd == data->hapd) continue; +#ifdef CONFIG_FST + /* Don't prune STAs belong to same FST */ + if (ohapd->iface->fst && + data->hapd->iface->fst && + fst_are_ifaces_aggregated(ohapd->iface->fst, + data->hapd->iface->fst)) + continue; +#endif /* CONFIG_FST */ osta = ap_get_sta(ohapd, data->addr); if (!osta) continue; + wpa_printf(MSG_INFO, "%s: Prune association for " MACSTR, + ohapd->conf->iface, MAC2STR(osta->addr)); ap_sta_disassociate(ohapd, osta, WLAN_REASON_UNSPECIFIED); } diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c index dc6501997db03..fd1c8ddacee62 100644 --- a/src/ap/vlan_init.c +++ b/src/ap/vlan_init.c @@ -9,6 +9,13 @@ */ #include "utils/includes.h" +#ifdef CONFIG_FULL_DYNAMIC_VLAN +#include <net/if.h> +#include <sys/ioctl.h> +#include <linux/sockios.h> +#include <linux/if_vlan.h> +#include <linux/if_bridge.h> +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ #include "utils/common.h" #include "hostapd.h" @@ -20,12 +27,6 @@ #ifdef CONFIG_FULL_DYNAMIC_VLAN -#include <net/if.h> -#include <sys/ioctl.h> -#include <linux/sockios.h> -#include <linux/if_vlan.h> -#include <linux/if_bridge.h> - #include "drivers/priv_netlink.h" #include "utils/eloop.h" @@ -34,6 +35,90 @@ struct full_dynamic_vlan { int s; /* socket on which to listen for new/removed interfaces. */ }; +#define DVLAN_CLEAN_BR 0x1 +#define DVLAN_CLEAN_VLAN 0x2 +#define DVLAN_CLEAN_VLAN_PORT 0x4 + +struct dynamic_iface { + char ifname[IFNAMSIZ + 1]; + int usage; + int clean; + struct dynamic_iface *next; +}; + + +/* Increment ref counter for ifname and add clean flag. + * If not in list, add it only if some flags are given. + */ +static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname, + int clean) +{ + struct dynamic_iface *next, **dynamic_ifaces; + struct hapd_interfaces *interfaces; + + interfaces = hapd->iface->interfaces; + dynamic_ifaces = &interfaces->vlan_priv; + + for (next = *dynamic_ifaces; next; next = next->next) { + if (os_strcmp(ifname, next->ifname) == 0) + break; + } + + if (next) { + next->usage++; + next->clean |= clean; + return; + } + + if (!clean) + return; + + next = os_zalloc(sizeof(*next)); + if (!next) + return; + os_strlcpy(next->ifname, ifname, sizeof(next->ifname)); + next->usage = 1; + next->clean = clean; + next->next = *dynamic_ifaces; + *dynamic_ifaces = next; +} + + +/* Decrement reference counter for given ifname. + * Return clean flag iff reference counter was decreased to zero, else zero + */ +static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname) +{ + struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces; + struct hapd_interfaces *interfaces; + int clean; + + interfaces = hapd->iface->interfaces; + dynamic_ifaces = &interfaces->vlan_priv; + + for (next = *dynamic_ifaces; next; next = next->next) { + if (os_strcmp(ifname, next->ifname) == 0) + break; + prev = next; + } + + if (!next) + return 0; + + next->usage--; + if (next->usage) + return 0; + + if (prev) + prev->next = next->next; + else + *dynamic_ifaces = next->next; + clean = next->clean; + os_free(next); + + return clean; +} + static int ifconfig_helper(const char *if_name, int up) { @@ -481,11 +566,13 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) struct hostapd_vlan *vlan = hapd->conf->vlan; char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; int vlan_naming = hapd->conf->ssid.vlan_naming; + int clean; wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); while (vlan) { - if (os_strcmp(ifname, vlan->ifname) == 0) { + if (os_strcmp(ifname, vlan->ifname) == 0 && !vlan->configured) { + vlan->configured = 1; if (hapd->conf->vlan_bridge[0]) { os_snprintf(br_name, sizeof(br_name), "%s%d", @@ -500,8 +587,8 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) "brvlan%d", vlan->vlan_id); } - if (!br_addbr(br_name)) - vlan->clean |= DVLAN_CLEAN_BR; + dyn_iface_get(hapd, br_name, + br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR); ifconfig_up(br_name); @@ -517,13 +604,16 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) sizeof(vlan_ifname), "vlan%d", vlan->vlan_id); + clean = 0; ifconfig_up(tagged_interface); if (!vlan_add(tagged_interface, vlan->vlan_id, vlan_ifname)) - vlan->clean |= DVLAN_CLEAN_VLAN; + clean |= DVLAN_CLEAN_VLAN; if (!br_addif(br_name, vlan_ifname)) - vlan->clean |= DVLAN_CLEAN_VLAN_PORT; + clean |= DVLAN_CLEAN_VLAN_PORT; + + dyn_iface_get(hapd, vlan_ifname, clean); ifconfig_up(vlan_ifname); } @@ -547,13 +637,15 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd) struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; int vlan_naming = hapd->conf->ssid.vlan_naming; + int clean; wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname); first = prev = vlan; while (vlan) { - if (os_strcmp(ifname, vlan->ifname) == 0) { + if (os_strcmp(ifname, vlan->ifname) == 0 && + vlan->configured) { if (hapd->conf->vlan_bridge[0]) { os_snprintf(br_name, sizeof(br_name), "%s%d", hapd->conf->vlan_bridge, @@ -581,20 +673,27 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd) os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vlan->vlan_id); - if (vlan->clean & DVLAN_CLEAN_VLAN_PORT) + + clean = dyn_iface_put(hapd, vlan_ifname); + + if (clean & DVLAN_CLEAN_VLAN_PORT) br_delif(br_name, vlan_ifname); - ifconfig_down(vlan_ifname); - if (vlan->clean & DVLAN_CLEAN_VLAN) + if (clean & DVLAN_CLEAN_VLAN) { + ifconfig_down(vlan_ifname); vlan_rem(vlan_ifname); + } } - if ((vlan->clean & DVLAN_CLEAN_BR) && + clean = dyn_iface_put(hapd, br_name); + if ((clean & DVLAN_CLEAN_BR) && br_getnumports(br_name) == 0) { ifconfig_down(br_name); br_delbr(br_name); } + } + if (os_strcmp(ifname, vlan->ifname) == 0) { if (vlan == first) { hapd->conf->vlan = vlan->next; } else { @@ -651,6 +750,11 @@ vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, if (!ifname[0]) return; + if (del && if_nametoindex(ifname)) { + /* interface still exists, race condition -> + * iface has just been recreated */ + return; + } wpa_printf(MSG_DEBUG, "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)", @@ -778,8 +882,7 @@ static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv) #endif /* CONFIG_FULL_DYNAMIC_VLAN */ -int vlan_setup_encryption_dyn(struct hostapd_data *hapd, - struct hostapd_ssid *mssid, const char *dyn_vlan) +int vlan_setup_encryption_dyn(struct hostapd_data *hapd, const char *dyn_vlan) { int i; @@ -789,10 +892,11 @@ int vlan_setup_encryption_dyn(struct hostapd_data *hapd, /* Static WEP keys are set here; IEEE 802.1X and WPA uses their own * functions for setting up dynamic broadcast keys. */ for (i = 0; i < 4; i++) { - if (mssid->wep.key[i] && + if (hapd->conf->ssid.wep.key[i] && hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i, - i == mssid->wep.idx, NULL, 0, - mssid->wep.key[i], mssid->wep.len[i])) + i == hapd->conf->ssid.wep.idx, NULL, 0, + hapd->conf->ssid.wep.key[i], + hapd->conf->ssid.wep.len[i])) { wpa_printf(MSG_ERROR, "VLAN: Could not set WEP " "encryption for dynamic VLAN"); @@ -953,7 +1057,8 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) return 1; - wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d)", __func__, vlan_id); + wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)", + __func__, hapd->conf->iface, vlan_id); vlan = hapd->conf->vlan; while (vlan) { @@ -967,8 +1072,12 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) if (vlan == NULL) return 1; - if (vlan->dynamic_vlan == 0) + if (vlan->dynamic_vlan == 0) { hostapd_vlan_if_remove(hapd, vlan->ifname); +#ifdef CONFIG_FULL_DYNAMIC_VLAN + vlan_dellink(vlan->ifname, hapd); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + } return 0; } diff --git a/src/ap/vlan_init.h b/src/ap/vlan_init.h index 781eaac441be2..fc39443e5d34b 100644 --- a/src/ap/vlan_init.h +++ b/src/ap/vlan_init.h @@ -18,7 +18,6 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, int vlan_id); int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id); int vlan_setup_encryption_dyn(struct hostapd_data *hapd, - struct hostapd_ssid *mssid, const char *dyn_vlan); #else /* CONFIG_NO_VLAN */ static inline int vlan_init(struct hostapd_data *hapd) @@ -43,7 +42,6 @@ static inline int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) } static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd, - struct hostapd_ssid *mssid, const char *dyn_vlan) { return -1; diff --git a/src/ap/vlan_util.c b/src/ap/vlan_util.c index cc54051b1ecad..d4e0efb9b0242 100644 --- a/src/ap/vlan_util.c +++ b/src/ap/vlan_util.c @@ -31,7 +31,7 @@ */ int vlan_add(const char *if_name, int vid, const char *vlan_if_name) { - int ret = -1; + int err, ret = -1; struct nl_sock *handle = NULL; struct nl_cache *cache = NULL; struct rtnl_link *rlink = NULL; @@ -58,14 +58,18 @@ int vlan_add(const char *if_name, int vid, const char *vlan_if_name) goto vlan_add_error; } - if (nl_connect(handle, NETLINK_ROUTE) < 0) { - wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink"); + err = nl_connect(handle, NETLINK_ROUTE); + if (err < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink: %s", + nl_geterror(err)); goto vlan_add_error; } - if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) { + err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache); + if (err < 0) { cache = NULL; - wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache"); + wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s", + nl_geterror(err)); goto vlan_add_error; } @@ -92,23 +96,29 @@ int vlan_add(const char *if_name, int vid, const char *vlan_if_name) goto vlan_add_error; } - if (rtnl_link_set_type(rlink, "vlan") < 0) { - wpa_printf(MSG_ERROR, "VLAN: failed to set link type"); + err = rtnl_link_set_type(rlink, "vlan"); + if (err < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to set link type: %s", + nl_geterror(err)); goto vlan_add_error; } rtnl_link_set_link(rlink, if_idx); rtnl_link_set_name(rlink, vlan_if_name); - if (rtnl_link_vlan_set_id(rlink, vid) < 0) { - wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id"); + err = rtnl_link_vlan_set_id(rlink, vid); + if (err < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id: %s", + nl_geterror(err)); goto vlan_add_error; } - if (rtnl_link_add(handle, rlink, NLM_F_CREATE) < 0) { + err = rtnl_link_add(handle, rlink, NLM_F_CREATE); + if (err < 0) { wpa_printf(MSG_ERROR, "VLAN: failed to create link %s for " - "vlan %d on %s (%d)", - vlan_if_name, vid, if_name, if_idx); + "vlan %d on %s (%d): %s", + vlan_if_name, vid, if_name, if_idx, + nl_geterror(err)); goto vlan_add_error; } @@ -127,7 +137,7 @@ vlan_add_error: int vlan_rem(const char *if_name) { - int ret = -1; + int err, ret = -1; struct nl_sock *handle = NULL; struct nl_cache *cache = NULL; struct rtnl_link *rlink = NULL; @@ -140,14 +150,18 @@ int vlan_rem(const char *if_name) goto vlan_rem_error; } - if (nl_connect(handle, NETLINK_ROUTE) < 0) { - wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink"); + err = nl_connect(handle, NETLINK_ROUTE); + if (err < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink: %s", + nl_geterror(err)); goto vlan_rem_error; } - if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) { + err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache); + if (err < 0) { cache = NULL; - wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache"); + wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s", + nl_geterror(err)); goto vlan_rem_error; } @@ -158,9 +172,10 @@ int vlan_rem(const char *if_name) goto vlan_rem_error; } - if (rtnl_link_delete(handle, rlink) < 0) { - wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s", - if_name); + err = rtnl_link_delete(handle, rlink); + if (err < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s: %s", + if_name, nl_geterror(err)); goto vlan_rem_error; } diff --git a/src/ap/wmm.c b/src/ap/wmm.c index 6d4177c2a847c..314e244bc956a 100644 --- a/src/ap/wmm.c +++ b/src/ap/wmm.c @@ -274,6 +274,9 @@ void hostapd_wmm_action(struct hostapd_data *hapd, return; } + if (left < 0) + return; /* not a valid WMM Action frame */ + /* extract the tspec info element */ if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 9c5f6094acbb8..2760a3f3a00e5 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -45,6 +45,12 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, const u8 *pmk, struct wpa_ptk *ptk); +static void wpa_group_free(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); +static void wpa_group_get(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); +static void wpa_group_put(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); static const u32 dot11RSNAConfigGroupUpdateCount = 4; static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; @@ -67,6 +73,14 @@ 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); +} + + static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, wpa_eapol_variable var, int value) @@ -254,15 +268,22 @@ static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; - struct wpa_group *group; + struct wpa_group *group, *next; wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK"); - for (group = wpa_auth->group; group; group = group->next) { + group = wpa_auth->group; + while (group) { + wpa_group_get(wpa_auth, group); + group->GTKReKey = TRUE; do { group->changed = FALSE; wpa_group_sm_step(wpa_auth, group); } while (group->changed); + + next = group->next; + wpa_group_put(wpa_auth, group); + group = next; } if (wpa_auth->conf.wpa_group_rekey) { @@ -565,6 +586,7 @@ wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, sm->wpa_auth = wpa_auth; sm->group = wpa_auth->group; + wpa_group_get(sm->wpa_auth, sm->group); return sm; } @@ -643,6 +665,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm) #endif /* CONFIG_IEEE80211R */ os_free(sm->last_rx_eapol_key); os_free(sm->wpa_ie); + wpa_group_put(sm->wpa_auth, sm->group); os_free(sm); } @@ -1517,6 +1540,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, else WPA_PUT_BE16(key->key_data_length, key_data_len); +#ifndef CONFIG_NO_RC4 } else if (sm->PTK.kek_len == 16) { u8 ek[32]; os_memcpy(key->key_iv, @@ -1532,6 +1556,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, else WPA_PUT_BE16(key->key_data_length, key_data_len); +#endif /* CONFIG_NO_RC4 */ } else { os_free(hdr); os_free(buf); @@ -1646,7 +1671,7 @@ void wpa_remove_ptk(struct wpa_state_machine *sm) } -int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) +int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) { int remove_ptk = 1; @@ -1734,6 +1759,14 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) wpa_remove_ptk(sm); } + if (sm->in_step_loop) { + /* + * wpa_sm_step() is already running - avoid recursive call to + * it by making the existing loop process the new update. + */ + sm->changed = TRUE; + return 0; + } return wpa_sm_step(sm); } @@ -1818,9 +1851,13 @@ static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth, group->reject_4way_hs_for_entropy = FALSE; } - wpa_group_init_gmk_and_counter(wpa_auth, group); - wpa_gtk_update(wpa_auth, group); - wpa_group_config_group_keys(wpa_auth, group); + if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0 || + wpa_gtk_update(wpa_auth, group) < 0 || + wpa_group_config_group_keys(wpa_auth, group) < 0) { + wpa_printf(MSG_INFO, "WPA: GMK/GTK setup failed"); + group->first_sta_seen = FALSE; + group->reject_4way_hs_for_entropy = TRUE; + } } @@ -1985,7 +2022,7 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) { struct wpa_ptk PTK; - int ok = 0; + int ok = 0, psk_found = 0; const u8 *pmk = NULL; SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); @@ -2001,6 +2038,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) sm->p2p_dev_addr, pmk); if (pmk == NULL) break; + psk_found = 1; } else pmk = sm->PMK; @@ -2020,6 +2058,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) if (!ok) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "invalid MIC in msg 2/4 of 4-Way Handshake"); + if (psk_found) + wpa_auth_psk_failure_report(sm->wpa_auth, sm->addr); return; } @@ -2983,9 +3023,9 @@ void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth) } -static const char * wpa_bool_txt(int bool) +static const char * wpa_bool_txt(int val) { - return bool ? "TRUE" : "FALSE"; + return val ? "TRUE" : "FALSE"; } @@ -3270,6 +3310,63 @@ void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, } +/* + * Remove and free the group from wpa_authenticator. This is triggered by a + * callback to make sure nobody is currently iterating the group list while it + * gets modified. + */ +static void wpa_group_free(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + struct wpa_group *prev = wpa_auth->group; + + wpa_printf(MSG_DEBUG, "WPA: Remove group state machine for VLAN-ID %d", + group->vlan_id); + + while (prev) { + if (prev->next == group) { + /* This never frees the special first group as needed */ + prev->next = group->next; + os_free(group); + break; + } + prev = prev->next; + } + +} + + +/* Increase the reference counter for group */ +static void wpa_group_get(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + /* Skip the special first group */ + if (wpa_auth->group == group) + return; + + group->references++; +} + + +/* Decrease the reference counter and maybe free the group */ +static void wpa_group_put(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + /* Skip the special first group */ + if (wpa_auth->group == group) + return; + + group->references--; + if (group->references) + return; + wpa_group_free(wpa_auth, group); +} + + +/* + * Add a group that has its references counter set to zero. Caller needs to + * call wpa_group_get() on the return value to mark the entry in use. + */ static struct wpa_group * wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) { @@ -3320,7 +3417,10 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state " "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id); + wpa_group_get(sm->wpa_auth, group); + wpa_group_put(sm->wpa_auth, sm->group); sm->group = group; + return 0; } diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 2788e657435d5..fd04f169433af 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-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -12,6 +12,9 @@ #include "common/defs.h" #include "common/eapol_common.h" #include "common/wpa_common.h" +#include "common/ieee802_11_defs.h" + +#define MAX_OWN_IE_OVERRIDE 256 #ifdef _MSC_VER #pragma pack(push, 1) @@ -146,8 +149,7 @@ struct wpa_auth_config { int group_mgmt_cipher; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R -#define SSID_LEN 32 - u8 ssid[SSID_LEN]; + 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]; @@ -164,6 +166,8 @@ struct wpa_auth_config { int ap_mlme; #ifdef CONFIG_TESTING_OPTIONS double corrupt_gtk_rekey_mic_probability; + u8 own_ie_override[MAX_OWN_IE_OVERRIDE]; + size_t own_ie_override_len; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_P2P u8 ip_addr_go[4]; @@ -189,6 +193,7 @@ struct wpa_auth_callbacks { const char *txt); void (*disconnect)(void *ctx, const u8 *addr, u16 reason); int (*mic_failure_report)(void *ctx, const u8 *addr); + void (*psk_failure_report)(void *ctx, const u8 *addr); void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var, int value); int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); @@ -251,12 +256,12 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm); void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u8 *data, size_t data_len); -typedef enum { +enum wpa_event { WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, WPA_REAUTH_EAPOL, WPA_ASSOC_FT -} wpa_event; +}; void wpa_remove_ptk(struct wpa_state_machine *sm); -int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event); +int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event); void wpa_auth_sm_notify(struct wpa_state_machine *sm); void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth); int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen); diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index ef3249a3eb9b4..eeaffbf635166 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -534,10 +534,8 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, return pos; } -#ifdef NEED_AP_MLME - if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) { + if (parse.wmm_tspec) { struct wmm_tspec_element *tspec; - int res; if (parse.wmm_tspec_len + 2 < (int) sizeof(*tspec)) { wpa_printf(MSG_DEBUG, "FT: Too short WMM TSPEC IE " @@ -555,7 +553,13 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, } tspec = (struct wmm_tspec_element *) pos; os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec)); - res = wmm_process_tspec(tspec); + } + +#ifdef NEED_AP_MLME + if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) { + int res; + + res = wmm_process_tspec((struct wmm_tspec_element *) pos); wpa_printf(MSG_DEBUG, "FT: ADDTS processing result: %d", res); if (res == WMM_ADDTS_STATUS_INVALID_PARAMETERS) rdie->status_code = @@ -566,20 +570,17 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, else { /* TSPEC accepted; include updated TSPEC in response */ rdie->descr_count = 1; - pos += sizeof(*tspec); + pos += sizeof(struct wmm_tspec_element); } return pos; } #endif /* NEED_AP_MLME */ if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) { - struct wmm_tspec_element *tspec; int res; - tspec = (struct wmm_tspec_element *) pos; - os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec)); res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos, - sizeof(*tspec)); + sizeof(struct wmm_tspec_element)); if (res >= 0) { if (res) rdie->status_code = host_to_le16(res); @@ -587,7 +588,7 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, /* TSPEC accepted; include updated TSPEC in * response */ rdie->descr_count = 1; - pos += sizeof(*tspec); + pos += sizeof(struct wmm_tspec_element); } return pos; } diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 7f8320708c396..f98cc50599e37 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -11,6 +11,7 @@ #include "utils/common.h" #include "common/ieee802_11_defs.h" #include "common/sae.h" +#include "common/wpa_ctrl.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" #include "eap_server/eap.h" @@ -53,8 +54,8 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R wconf->ssid_len = conf->ssid.ssid_len; - if (wconf->ssid_len > SSID_LEN) - wconf->ssid_len = SSID_LEN; + if (wconf->ssid_len > SSID_MAX_LEN) + wconf->ssid_len = SSID_MAX_LEN; os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len); os_memcpy(wconf->mobility_domain, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN); @@ -91,6 +92,13 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, #ifdef CONFIG_TESTING_OPTIONS wconf->corrupt_gtk_rekey_mic_probability = iconf->corrupt_gtk_rekey_mic_probability; + if (conf->own_ie_override && + wpabuf_len(conf->own_ie_override) <= MAX_OWN_IE_OVERRIDE) { + wconf->own_ie_override_len = wpabuf_len(conf->own_ie_override); + os_memcpy(wconf->own_ie_override, + wpabuf_head(conf->own_ie_override), + wconf->own_ie_override_len); + } #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_P2P os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4); @@ -144,6 +152,14 @@ static int hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr) } +static void hostapd_wpa_auth_psk_failure_report(void *ctx, const u8 *addr) +{ + struct hostapd_data *hapd = ctx; + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR, + MAC2STR(addr)); +} + + static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr, wpa_eapol_variable var, int value) { @@ -579,6 +595,7 @@ int hostapd_setup_wpa(struct hostapd_data *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; @@ -620,7 +637,8 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) } #ifdef CONFIG_IEEE80211R - if (!hostapd_drv_none(hapd)) { + if (!hostapd_drv_none(hapd) && hapd->conf->ft_over_ds && + 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, diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index 7b2cd3ea8ed44..57b098f2ed72a 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -169,6 +169,8 @@ struct wpa_group { u8 IGTK[2][WPA_IGTK_MAX_LEN]; int GN_igtk, GM_igtk; #endif /* CONFIG_IEEE80211W */ + /* Number of references except those in struct wpa_group->next */ + unsigned int references; }; diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index f2872970affee..eafb828b8d60e 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -261,7 +261,8 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, } #ifdef CONFIG_IEEE80211W - if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION && + conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) { if (pos + 2 + 4 > buf + len) return -1; if (pmkid == NULL) { @@ -377,6 +378,23 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) u8 *pos, buf[128]; int res; +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_auth->conf.own_ie_override_len) { + wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing", + wpa_auth->conf.own_ie_override, + wpa_auth->conf.own_ie_override_len); + os_free(wpa_auth->wpa_ie); + wpa_auth->wpa_ie = + os_malloc(wpa_auth->conf.own_ie_override_len); + if (wpa_auth->wpa_ie == NULL) + return -1; + os_memcpy(wpa_auth->wpa_ie, wpa_auth->conf.own_ie_override, + wpa_auth->conf.own_ie_override_len); + wpa_auth->wpa_ie_len = wpa_auth->conf.own_ie_override_len; + return 0; + } +#endif /* CONFIG_TESTING_OPTIONS */ + pos = buf; if (wpa_auth->conf.wpa == WPA_PROTO_OSEN) { diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index b0e8b0bfcac34..cde31e60e03b7 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -324,7 +324,7 @@ static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd, wpa_printf(MSG_DEBUG, "WPS: Updating in-memory configuration"); bss->wps_state = 2; - if (cred->ssid_len <= HOSTAPD_MAX_SSID_LEN) { + if (cred->ssid_len <= SSID_MAX_LEN) { os_memcpy(bss->ssid.ssid, cred->ssid, cred->ssid_len); bss->ssid.ssid_len = cred->ssid_len; bss->ssid.ssid_set = 1; @@ -347,8 +347,12 @@ static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd, bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; bss->wpa_pairwise = 0; - if (cred->encr_type & WPS_ENCR_AES) - bss->wpa_pairwise |= WPA_CIPHER_CCMP; + if (cred->encr_type & WPS_ENCR_AES) { + if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD) + bss->wpa_pairwise |= WPA_CIPHER_GCMP; + else + bss->wpa_pairwise |= WPA_CIPHER_CCMP; + } if (cred->encr_type & WPS_ENCR_TKIP) bss->wpa_pairwise |= WPA_CIPHER_TKIP; bss->rsn_pairwise = bss->wpa_pairwise; @@ -448,6 +452,11 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) os_free(hapd->wps->network_key); hapd->wps->network_key = NULL; hapd->wps->network_key_len = 0; + } else if ((cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) && + (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN)) { + wpa_printf(MSG_INFO, "WPS: Invalid key length %lu for WPA/WPA2", + (unsigned long) cred->key_len); + return -1; } else { if (hapd->wps->network_key == NULL || hapd->wps->network_key_len < cred->key_len) { @@ -530,7 +539,11 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) fprintf(nconf, "wpa_pairwise="); prefix = ""; if (cred->encr_type & WPS_ENCR_AES) { - fprintf(nconf, "CCMP"); + if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD) + fprintf(nconf, "GCMP"); + else + fprintf(nconf, "CCMP"); + prefix = " "; } if (cred->encr_type & WPS_ENCR_TKIP) { @@ -844,7 +857,9 @@ static int hostapd_wps_rf_band_cb(void *ctx) struct hostapd_data *hapd = ctx; return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? - WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ + WPS_RF_50GHZ : + hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ? + WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ } @@ -856,8 +871,10 @@ static void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only) wpabuf_free(hapd->wps_probe_resp_ie); hapd->wps_probe_resp_ie = NULL; - if (deinit_only) + if (deinit_only) { + hostapd_reset_ap_wps_ie(hapd); return; + } hostapd_set_ap_wps_ie(hapd); } @@ -1039,7 +1056,9 @@ int hostapd_init_wps(struct hostapd_data *hapd, } else { wps->dev.rf_bands = hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? - WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ + WPS_RF_50GHZ : + hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ? + WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ } if (conf->wpa & WPA_PROTO_RSN) { @@ -1285,30 +1304,53 @@ int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr, } +struct wps_button_pushed_ctx { + const u8 *p2p_dev_addr; + unsigned int count; +}; + static int wps_button_pushed(struct hostapd_data *hapd, void *ctx) { - const u8 *p2p_dev_addr = ctx; - if (hapd->wps == NULL) - return -1; - return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr); + struct wps_button_pushed_ctx *data = ctx; + + if (hapd->wps) { + data->count++; + return wps_registrar_button_pushed(hapd->wps->registrar, + data->p2p_dev_addr); + } + + return 0; } int hostapd_wps_button_pushed(struct hostapd_data *hapd, const u8 *p2p_dev_addr) { - return hostapd_wps_for_each(hapd, wps_button_pushed, - (void *) p2p_dev_addr); + struct wps_button_pushed_ctx ctx; + int ret; + + os_memset(&ctx, 0, sizeof(ctx)); + ctx.p2p_dev_addr = p2p_dev_addr; + ret = hostapd_wps_for_each(hapd, wps_button_pushed, &ctx); + if (ret == 0 && !ctx.count) + ret = -1; + return ret; } +struct wps_cancel_ctx { + unsigned int count; +}; + static int wps_cancel(struct hostapd_data *hapd, void *ctx) { - if (hapd->wps == NULL) - return -1; + struct wps_cancel_ctx *data = ctx; - wps_registrar_wps_cancel(hapd->wps->registrar); - ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL); + if (hapd->wps) { + data->count++; + wps_registrar_wps_cancel(hapd->wps->registrar); + ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL); + } return 0; } @@ -1316,7 +1358,14 @@ static int wps_cancel(struct hostapd_data *hapd, void *ctx) int hostapd_wps_cancel(struct hostapd_data *hapd) { - return hostapd_wps_for_each(hapd, wps_cancel, NULL); + struct wps_cancel_ctx ctx; + int ret; + + os_memset(&ctx, 0, sizeof(ctx)); + ret = hostapd_wps_for_each(hapd, wps_cancel, &ctx); + if (ret == 0 && !ctx.count) + ret = -1; + return ret; } @@ -1546,6 +1595,10 @@ struct wps_ap_pin_data { static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx) { struct wps_ap_pin_data *data = ctx; + + if (!hapd->wps) + return 0; + os_free(hapd->conf->ap_pin); hapd->conf->ap_pin = os_strdup(data->pin_txt); #ifdef CONFIG_WPS_UPNP diff --git a/src/ap/x_snoop.c b/src/ap/x_snoop.c index 8f77015ef57fb..aef9a53c46cf9 100644 --- a/src/ap/x_snoop.c +++ b/src/ap/x_snoop.c @@ -51,6 +51,14 @@ int x_snoop_init(struct hostapd_data *hapd) return -1; } +#ifdef CONFIG_IPV6 + if (hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, 1)) { + wpa_printf(MSG_DEBUG, + "x_snoop: Failed to enable multicast snooping on the bridge"); + return -1; + } +#endif /* CONFIG_IPV6 */ + return 0; } diff --git a/src/common/Makefile b/src/common/Makefile index adfd3dfd5b9be..e703630835e1f 100644 --- a/src/common/Makefile +++ b/src/common/Makefile @@ -1,8 +1,28 @@ -all: - @echo Nothing to be made. +all: libcommon.a clean: - rm -f *~ *.o *.d *.gcno *.gcda *.gcov + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libcommon.a install: @echo Nothing to be made. + +include ../lib.rules + +CFLAGS += -DCONFIG_IEEE80211R +CFLAGS += -DCONFIG_IEEE80211W +CFLAGS += -DCONFIG_HS20 +CFLAGS += -DCONFIG_SAE +CFLAGS += -DCONFIG_SUITE +CFLAGS += -DCONFIG_SUITEB + +LIB_OBJS= \ + gas.o \ + hw_features_common.o \ + ieee802_11_common.o \ + sae.o \ + wpa_common.o + +libcommon.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c index 56b11220c9ed4..d69448bd3800c 100644 --- a/src/common/common_module_tests.c +++ b/src/common/common_module_tests.c @@ -1,6 +1,6 @@ /* * common module tests - * Copyright (c) 2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2014-2015, 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,8 @@ #include "utils/common.h" #include "ieee802_11_common.h" +#include "ieee802_11_defs.h" +#include "gas.h" #include "wpa_common.h" @@ -46,6 +48,10 @@ static const struct ieee802_11_parse_test_data parse_tests[] = { { (u8 *) "\x6e\x00", 2, ParseOK, 1 }, { (u8 *) "\xc7\x00", 2, ParseOK, 1 }, { (u8 *) "\xc7\x01\x00", 3, ParseOK, 1 }, + { (u8 *) "\x03\x00\x2a\x00\x36\x00\x37\x00\x38\x00\x2d\x00\x3d\x00\xbf\x00\xc0\x00", + 18, ParseOK, 9 }, + { (u8 *) "\x8b\x00", 2, ParseOK, 1 }, + { (u8 *) "\xdd\x04\x00\x90\x4c\x04", 6, ParseUnknown, 1 }, { NULL, 0, ParseOK, 0 } }; @@ -158,6 +164,34 @@ static int rsn_ie_parse_tests(void) } +static int gas_tests(void) +{ + struct wpabuf *buf; + + wpa_printf(MSG_INFO, "gas tests"); + gas_anqp_set_len(NULL); + + buf = wpabuf_alloc(1); + if (buf == NULL) + return -1; + gas_anqp_set_len(buf); + wpabuf_free(buf); + + buf = wpabuf_alloc(20); + if (buf == NULL) + return -1; + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_REQ); + wpabuf_put_u8(buf, 0); + wpabuf_put_be32(buf, 0); + wpabuf_put_u8(buf, 0); + gas_anqp_set_len(buf); + wpabuf_free(buf); + + return 0; +} + + int common_module_tests(void) { int ret = 0; @@ -165,6 +199,7 @@ int common_module_tests(void) wpa_printf(MSG_INFO, "common module tests"); if (ieee802_11_parse_tests() < 0 || + gas_tests() < 0 || rsn_ie_parse_tests() < 0) ret = -1; diff --git a/src/common/defs.h b/src/common/defs.h index b5f4f801eda21..6aea3751a2bc9 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -174,7 +174,7 @@ enum wpa_states { /** * WPA_INTERFACE_DISABLED - Interface disabled * - * This stat eis entered if the network interface is disabled, e.g., + * This state is entered if the network interface is disabled, e.g., * due to rfkill. wpa_supplicant refuses any new operations that would * use the radio until the interface has been enabled. */ @@ -295,6 +295,7 @@ enum hostapd_hw_mode { HOSTAPD_MODE_IEEE80211G, HOSTAPD_MODE_IEEE80211A, HOSTAPD_MODE_IEEE80211AD, + HOSTAPD_MODE_IEEE80211ANY, NUM_HOSTAPD_MODES }; @@ -310,6 +311,7 @@ enum wpa_ctrl_req_type { WPA_CTRL_REQ_EAP_OTP, WPA_CTRL_REQ_EAP_PASSPHRASE, WPA_CTRL_REQ_SIM, + WPA_CTRL_REQ_PSK_PASSPHRASE, NUM_WPA_CTRL_REQS }; @@ -326,4 +328,10 @@ enum mesh_plink_state { PLINK_BLOCKED, }; +enum set_band { + WPA_SETBAND_AUTO, + WPA_SETBAND_5G, + WPA_SETBAND_2G +}; + #endif /* DEFS_H */ diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c index e8babb52a9c56..9c37ea63ca878 100644 --- a/src/common/hw_features_common.c +++ b/src/common/hw_features_common.c @@ -88,8 +88,8 @@ int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan, int sec_chan) { int ok, j, first; - int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, - 184, 192 }; + int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140, + 149, 157, 184, 192 }; size_t k; if (pri_chan == sec_chan || !sec_chan) @@ -152,8 +152,7 @@ void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan) *pri_chan = *sec_chan = 0; ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); - if (elems.ht_operation && - elems.ht_operation_len >= sizeof(*oper)) { + if (elems.ht_operation) { oper = (struct ieee80211_ht_operation *) elems.ht_operation; *pri_chan = oper->primary_chan; if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) { @@ -177,10 +176,8 @@ int check_40mhz_5g(struct hostapd_hw_modes *mode, size_t i; int match; - if (!mode || !scan_res || !pri_chan || !sec_chan) - return 0; - - if (pri_chan == sec_chan) + if (!mode || !scan_res || !pri_chan || !sec_chan || + pri_chan == sec_chan) return 0; pri_freq = hw_get_freq(mode, pri_chan); @@ -238,7 +235,8 @@ int check_40mhz_5g(struct hostapd_hw_modes *mode, } -int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end) +static int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, + int end) { struct ieee802_11_elems elems; struct ieee80211_ht_operation *oper; @@ -253,8 +251,7 @@ int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end) return 1; } - if (elems.ht_operation && - elems.ht_operation_len >= sizeof(*oper)) { + if (elems.ht_operation) { oper = (struct ieee80211_ht_operation *) elems.ht_operation; if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK) return 0; @@ -275,10 +272,8 @@ int check_40mhz_2g4(struct hostapd_hw_modes *mode, int affected_start, affected_end; size_t i; - if (!mode || !scan_res || !pri_chan || !sec_chan) - return 0; - - if (pri_chan == sec_chan) + if (!mode || !scan_res || !pri_chan || !sec_chan || + pri_chan == sec_chan) return 0; pri_freq = hw_get_freq(mode, pri_chan); @@ -335,9 +330,7 @@ int check_40mhz_2g4(struct hostapd_hw_modes *mode, ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); - if (elems.ht_capabilities && - elems.ht_capabilities_len >= - sizeof(struct ieee80211_ht_capabilities)) { + if (elems.ht_capabilities) { struct ieee80211_ht_capabilities *ht_cap = (struct ieee80211_ht_capabilities *) elems.ht_capabilities; @@ -363,8 +356,6 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, int vht_oper_chwidth, int center_segment0, int center_segment1, u32 vht_caps) { - int tmp; - os_memset(data, 0, sizeof(*data)); data->mode = mode; data->freq = freq; @@ -378,11 +369,10 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, if (data->vht_enabled) switch (vht_oper_chwidth) { case VHT_CHANWIDTH_USE_HT: - if (center_segment1) - return -1; - if (center_segment0 != 0 && - 5000 + center_segment0 * 5 != data->center_freq1 && - 2407 + center_segment0 * 5 != data->center_freq1) + if (center_segment1 || + (center_segment0 != 0 && + 5000 + center_segment0 * 5 != data->center_freq1 && + 2407 + center_segment0 * 5 != data->center_freq1)) return -1; break; case VHT_CHANWIDTH_80P80MHZ: @@ -398,19 +388,38 @@ 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) + if ((vht_oper_chwidth == 1 && center_segment1) || + (vht_oper_chwidth == 3 && !center_segment1) || + !sec_channel_offset) return -1; - if (vht_oper_chwidth == 3 && !center_segment1) - return -1; - if (!sec_channel_offset) - return -1; - /* primary 40 part must match the HT configuration */ - tmp = (30 + freq - 5000 - center_segment0 * 5) / 20; - tmp /= 2; - if (data->center_freq1 != 5000 + - center_segment0 * 5 - 20 + 40 * tmp) - return -1; - data->center_freq1 = 5000 + center_segment0 * 5; + if (!center_segment0) { + if (channel <= 48) + center_segment0 = 42; + else if (channel <= 64) + center_segment0 = 58; + else if (channel <= 112) + center_segment0 = 106; + else if (channel <= 128) + center_segment0 = 122; + else if (channel <= 144) + center_segment0 = 138; + else if (channel <= 161) + center_segment0 = 155; + data->center_freq1 = 5000 + center_segment0 * 5; + } else { + /* + * Note: HT/VHT config and params are coupled. Check if + * HT40 channel band is in VHT80 Pri channel band + * configuration. + */ + if (center_segment0 == channel + 6 || + center_segment0 == channel + 2 || + center_segment0 == channel - 2 || + center_segment0 == channel - 6) + data->center_freq1 = 5000 + center_segment0 * 5; + else + return -1; + } break; case VHT_CHANWIDTH_160MHZ: data->bandwidth = 160; @@ -424,13 +433,21 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, return -1; if (!sec_channel_offset) return -1; - /* primary 40 part must match the HT configuration */ - tmp = (70 + freq - 5000 - center_segment0 * 5) / 20; - tmp /= 2; - if (data->center_freq1 != 5000 + - center_segment0 * 5 - 60 + 40 * tmp) + /* + * Note: HT/VHT config and params are coupled. Check if + * HT40 channel band is in VHT160 channel band configuration. + */ + if (center_segment0 == channel + 14 || + center_segment0 == channel + 10 || + center_segment0 == channel + 6 || + center_segment0 == channel + 2 || + center_segment0 == channel - 2 || + center_segment0 == channel - 6 || + center_segment0 == channel - 10 || + center_segment0 == channel - 14) + data->center_freq1 = 5000 + center_segment0 * 5; + else return -1; - data->center_freq1 = 5000 + center_segment0 * 5; break; } diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h index 7f43d00c5b275..7360b4e3efede 100644 --- a/src/common/hw_features_common.h +++ b/src/common/hw_features_common.h @@ -26,7 +26,6 @@ void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan); int check_40mhz_5g(struct hostapd_hw_modes *mode, struct wpa_scan_results *scan_res, int pri_chan, int sec_chan); -int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end); int check_40mhz_2g4(struct hostapd_hw_modes *mode, struct wpa_scan_results *scan_res, int pri_chan, int sec_chan); diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index aca0b73223bb8..d07a316a79298 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -1,6 +1,6 @@ /* * IEEE 802.11 Common routines - * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2015, 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,8 @@ #include "common.h" #include "defs.h" +#include "wpa_common.h" +#include "qca-vendor.h" #include "ieee802_11_defs.h" #include "ieee802_11_common.h" @@ -146,6 +148,20 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, } break; + case OUI_QCA: + switch (pos[3]) { + case QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST: + elems->pref_freq_list = pos; + elems->pref_freq_list_len = elen; + break; + default: + wpa_printf(MSG_EXCESSIVE, + "Unknown QCA information element ignored (type=%d len=%lu)", + pos[3], (unsigned long) elen); + return -1; + } + break; + default: wpa_printf(MSG_EXCESSIVE, "unknown vendor specific " "information element ignored (vendor OUI " @@ -196,6 +212,12 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, switch (id) { case WLAN_EID_SSID: + if (elen > SSID_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "Ignored too long SSID element (elen=%u)", + elen); + break; + } elems->ssid = pos; elems->ssid_len = elen; break; @@ -204,8 +226,9 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->supp_rates_len = elen; break; case WLAN_EID_DS_PARAMS: + if (elen < 1) + break; elems->ds_params = pos; - elems->ds_params_len = elen; break; case WLAN_EID_CF_PARAMS: case WLAN_EID_TIM: @@ -215,8 +238,9 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->challenge_len = elen; break; case WLAN_EID_ERP_INFO: + if (elen < 1) + break; elems->erp_info = pos; - elems->erp_info_len = elen; break; case WLAN_EID_EXT_SUPP_RATES: elems->ext_supp_rates = pos; @@ -239,24 +263,31 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->supp_channels_len = elen; break; case WLAN_EID_MOBILITY_DOMAIN: + if (elen < sizeof(struct rsn_mdie)) + break; elems->mdie = pos; elems->mdie_len = elen; break; case WLAN_EID_FAST_BSS_TRANSITION: + if (elen < sizeof(struct rsn_ftie)) + break; elems->ftie = pos; elems->ftie_len = elen; break; case WLAN_EID_TIMEOUT_INTERVAL: + if (elen != 5) + break; elems->timeout_int = pos; - elems->timeout_int_len = elen; break; case WLAN_EID_HT_CAP: + if (elen < sizeof(struct ieee80211_ht_capabilities)) + break; elems->ht_capabilities = pos; - elems->ht_capabilities_len = elen; break; case WLAN_EID_HT_OPERATION: + if (elen < sizeof(struct ieee80211_ht_operation)) + break; elems->ht_operation = pos; - elems->ht_operation_len = elen; break; case WLAN_EID_MESH_CONFIG: elems->mesh_config = pos; @@ -271,12 +302,14 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->peer_mgmt_len = elen; break; case WLAN_EID_VHT_CAP: + if (elen < sizeof(struct ieee80211_vht_capabilities)) + break; elems->vht_capabilities = pos; - elems->vht_capabilities_len = elen; break; case WLAN_EID_VHT_OPERATION: + if (elen < sizeof(struct ieee80211_vht_operation)) + break; elems->vht_operation = pos; - elems->vht_operation_len = elen; break; case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION: if (elen != 1) @@ -321,6 +354,18 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, /* after mic everything is encrypted, so stop. */ left = elen; break; + case WLAN_EID_MULTI_BAND: + if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) { + wpa_printf(MSG_MSGDUMP, + "IEEE 802.11 element parse ignored MB IE (id=%d elen=%d)", + id, elen); + break; + } + + elems->mb_ies.ies[elems->mb_ies.nof_ies].ie = pos; + elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen; + elems->mb_ies.nof_ies++; + break; default: unknown++; if (!show_errors) @@ -486,14 +531,14 @@ int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], ac->aifs = v; } else if (os_strcmp(pos, "cwmin") == 0) { v = atoi(val); - if (v < 0 || v > 12) { + if (v < 0 || v > 15) { wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v); return -1; } ac->cwmin = v; } else if (os_strcmp(pos, "cwmax") == 0) { v = atoi(val); - if (v < 0 || v > 12) { + if (v < 0 || v > 15) { wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v); return -1; } @@ -523,50 +568,163 @@ int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) { - enum hostapd_hw_mode mode = NUM_HOSTAPD_MODES; + u8 op_class; + + return ieee80211_freq_to_channel_ext(freq, 0, 0, &op_class, channel); +} + + +/** + * ieee80211_freq_to_channel_ext - Convert frequency into channel info + * for HT40 and VHT. DFS channels are not covered. + * @freq: Frequency (MHz) to convert + * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below + * @vht: 0 - non-VHT, 1 - 80 MHz + * @op_class: Buffer for returning operating class + * @channel: Buffer for returning channel number + * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure + */ +enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, + int sec_channel, int vht, + u8 *op_class, u8 *channel) +{ + /* TODO: more operating classes */ + + if (sec_channel > 1 || sec_channel < -1) + return NUM_HOSTAPD_MODES; if (freq >= 2412 && freq <= 2472) { - mode = HOSTAPD_MODE_IEEE80211G; + if ((freq - 2407) % 5) + return NUM_HOSTAPD_MODES; + + if (vht) + return NUM_HOSTAPD_MODES; + + /* 2.407 GHz, channels 1..13 */ + if (sec_channel == 1) + *op_class = 83; + else if (sec_channel == -1) + *op_class = 84; + else + *op_class = 81; + *channel = (freq - 2407) / 5; - } else if (freq == 2484) { - mode = HOSTAPD_MODE_IEEE80211B; + + return HOSTAPD_MODE_IEEE80211G; + } + + if (freq == 2484) { + if (sec_channel || vht) + return NUM_HOSTAPD_MODES; + + *op_class = 82; /* channel 14 */ *channel = 14; - } else if (freq >= 4900 && freq < 5000) { - mode = HOSTAPD_MODE_IEEE80211A; + + return HOSTAPD_MODE_IEEE80211B; + } + + if (freq >= 4900 && freq < 5000) { + if ((freq - 4000) % 5) + return NUM_HOSTAPD_MODES; *channel = (freq - 4000) / 5; - } else if (freq >= 5000 && freq < 5900) { - mode = HOSTAPD_MODE_IEEE80211A; + *op_class = 0; /* TODO */ + return HOSTAPD_MODE_IEEE80211A; + } + + /* 5 GHz, channels 36..48 */ + if (freq >= 5180 && freq <= 5240) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + + if (sec_channel == 1) + *op_class = 116; + else if (sec_channel == -1) + *op_class = 117; + else if (vht) + *op_class = 128; + else + *op_class = 115; + + *channel = (freq - 5000) / 5; + + return HOSTAPD_MODE_IEEE80211A; + } + + /* 5 GHz, channels 149..161 */ + if (freq >= 5745 && freq <= 5805) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + + if (sec_channel == 1) + *op_class = 126; + else if (sec_channel == -1) + *op_class = 127; + else if (vht) + *op_class = 128; + else + *op_class = 124; + *channel = (freq - 5000) / 5; - } else if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) { - mode = HOSTAPD_MODE_IEEE80211AD; + + return HOSTAPD_MODE_IEEE80211A; + } + + /* 5 GHz, channels 149..169 */ + if (freq >= 5745 && freq <= 5845) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + + *op_class = 125; + + *channel = (freq - 5000) / 5; + + return HOSTAPD_MODE_IEEE80211A; + } + + if (freq >= 5000 && freq < 5900) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + *channel = (freq - 5000) / 5; + *op_class = 0; /* TODO */ + return HOSTAPD_MODE_IEEE80211A; + } + + /* 56.16 GHz, channel 1..4 */ + if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) { + if (sec_channel || vht) + return NUM_HOSTAPD_MODES; + *channel = (freq - 56160) / 2160; + *op_class = 180; + + return HOSTAPD_MODE_IEEE80211AD; } - return mode; + return NUM_HOSTAPD_MODES; } -static const char *us_op_class_cc[] = { +static const char *const us_op_class_cc[] = { "US", "CA", NULL }; -static const char *eu_op_class_cc[] = { +static const char *const eu_op_class_cc[] = { "AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE", "DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT", "LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT", "RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", NULL }; -static const char *jp_op_class_cc[] = { +static const char *const jp_op_class_cc[] = { "JP", NULL }; -static const char *cn_op_class_cc[] = { - "CN", "CA", NULL +static const char *const cn_op_class_cc[] = { + "CN", NULL }; -static int country_match(const char *cc[], const char *country) +static int country_match(const char *const cc[], const char *const country) { int i; @@ -612,6 +770,10 @@ static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan) if (chan < 149 || chan > 161) return -1; return 5000 + 5 * chan; + case 5: /* channels 149,153,157,161,165 */ + if (chan < 149 || chan > 165) + return -1; + return 5000 + 5 * chan; case 34: /* 60 GHz band, channels 1..3 */ if (chan < 1 || chan > 3) return -1; @@ -764,12 +926,15 @@ static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan) return -1; return 5000 + 5 * chan; case 124: /* channels 149,153,157,161 */ - case 125: /* channels 149,153,157,161,165,169 */ case 126: /* channels 149,157; 40 MHz */ case 127: /* channels 153,161; 40 MHz */ if (chan < 149 || chan > 161) return -1; return 5000 + 5 * chan; + case 125: /* channels 149,153,157,161,165,169 */ + if (chan < 149 || chan > 169) + return -1; + return 5000 + 5 * chan; case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ if (chan < 36 || chan > 161) @@ -921,3 +1086,62 @@ const char * fc2str(u16 fc) return "WLAN_FC_TYPE_UNKNOWN"; #undef C2S } + + +int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, + size_t ies_len) +{ + os_memset(info, 0, sizeof(*info)); + + while (ies_buf && ies_len >= 2 && + info->nof_ies < MAX_NOF_MB_IES_SUPPORTED) { + size_t len = 2 + ies_buf[1]; + + if (len > ies_len) { + wpa_hexdump(MSG_DEBUG, "Truncated IEs", + ies_buf, ies_len); + return -1; + } + + if (ies_buf[0] == WLAN_EID_MULTI_BAND) { + wpa_printf(MSG_DEBUG, "MB IE of %zu bytes found", len); + info->ies[info->nof_ies].ie = ies_buf + 2; + info->ies[info->nof_ies].ie_len = ies_buf[1]; + info->nof_ies++; + } + + ies_len -= len; + ies_buf += len; + } + + return 0; +} + + +struct wpabuf * mb_ies_by_info(struct mb_ies_info *info) +{ + struct wpabuf *mb_ies = NULL; + + WPA_ASSERT(info != NULL); + + if (info->nof_ies) { + u8 i; + size_t mb_ies_size = 0; + + for (i = 0; i < info->nof_ies; i++) + mb_ies_size += 2 + info->ies[i].ie_len; + + mb_ies = wpabuf_alloc(mb_ies_size); + if (mb_ies) { + for (i = 0; i < info->nof_ies; i++) { + wpabuf_put_u8(mb_ies, WLAN_EID_MULTI_BAND); + wpabuf_put_u8(mb_ies, info->ies[i].ie_len); + wpabuf_put_data(mb_ies, + info->ies[i].ie, + info->ies[i].ie_len); + } + } + } + + return mb_ies; +} diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index 7f0b296d2b00a..55ce0223d9235 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -9,6 +9,16 @@ #ifndef IEEE802_11_COMMON_H #define IEEE802_11_COMMON_H +#define MAX_NOF_MB_IES_SUPPORTED 5 + +struct mb_ies_info { + struct { + const u8 *ie; + u8 ie_len; + } ies[MAX_NOF_MB_IES_SUPPORTED]; + u8 nof_ies; +}; + /* Parsed Information Elements */ struct ieee802_11_elems { const u8 *ssid; @@ -48,12 +58,11 @@ struct ieee802_11_elems { const u8 *osen; const u8 *ampe; const u8 *mic; + const u8 *pref_freq_list; u8 ssid_len; u8 supp_rates_len; - u8 ds_params_len; u8 challenge_len; - u8 erp_info_len; u8 ext_supp_rates_len; u8 wpa_ie_len; u8 rsn_ie_len; @@ -63,14 +72,9 @@ struct ieee802_11_elems { u8 supp_channels_len; u8 mdie_len; u8 ftie_len; - u8 timeout_int_len; - u8 ht_capabilities_len; - u8 ht_operation_len; u8 mesh_config_len; u8 mesh_id_len; u8 peer_mgmt_len; - u8 vht_capabilities_len; - u8 vht_operation_len; u8 vendor_ht_cap_len; u8 vendor_vht_len; u8 p2p_len; @@ -83,6 +87,8 @@ struct ieee802_11_elems { u8 osen_len; u8 ampe_len; u8 mic_len; + u8 pref_freq_list_len; + struct mb_ies_info mb_ies; }; typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; @@ -108,9 +114,15 @@ int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], const char *name, const char *val); enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel); 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 supp_rates_11b_only(struct ieee802_11_elems *elems); +int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, + size_t ies_len); +struct wpabuf * mb_ies_by_info(struct mb_ies_info *info); const char * fc2str(u16 fc); #endif /* IEEE802_11_COMMON_H */ diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 2e51935b8e3bd..44530ce3cee6a 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -10,6 +10,8 @@ #ifndef IEEE802_11_DEFS_H #define IEEE802_11_DEFS_H +#include <utils/common.h> + /* IEEE 802.11 defines */ #define WLAN_FC_PVER 0x0003 @@ -163,7 +165,10 @@ #define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76 #define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77 #define WLAN_STATUS_TRANSMISSION_FAILURE 79 +#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION 82 +#define WLAN_STATUS_PENDING_ADMITTING_FST_SESSION 86 #define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95 +#define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99 #define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104 /* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */ @@ -269,6 +274,8 @@ #define WLAN_EID_AMPE 139 #define WLAN_EID_MIC 140 #define WLAN_EID_CCKM 156 +#define WLAN_EID_MULTI_BAND 158 +#define WLAN_EID_SESSION_TRANSITION 164 #define WLAN_EID_VHT_CAP 191 #define WLAN_EID_VHT_OPERATION 192 #define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193 @@ -297,6 +304,7 @@ #define WLAN_ACTION_TDLS 12 #define WLAN_ACTION_SELF_PROTECTED 15 #define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */ +#define WLAN_ACTION_FST 18 #define WLAN_ACTION_VENDOR_SPECIFIC 127 /* Public action codes */ @@ -470,35 +478,35 @@ struct ieee80211_mgmt { le16 auth_transaction; le16 status_code; /* possibly followed by Challenge text */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED auth; struct { le16 reason_code; - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED deauth; struct { le16 capab_info; le16 listen_interval; /* followed by SSID and Supported rates */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED assoc_req; struct { le16 capab_info; le16 status_code; le16 aid; /* followed by Supported rates */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED assoc_resp, reassoc_resp; struct { le16 capab_info; le16 listen_interval; u8 current_ap[6]; /* followed by SSID and Supported rates */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED reassoc_req; struct { le16 reason_code; - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED disassoc; struct { u8 timestamp[8]; @@ -506,7 +514,7 @@ struct ieee80211_mgmt { le16 capab_info; /* followed by some of SSID, Supported rates, * FH Params, DS Params, CF Params, IBSS Params, TIM */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED beacon; struct { /* only variable items: SSID, Supported rates */ @@ -518,7 +526,7 @@ struct ieee80211_mgmt { le16 capab_info; /* followed by some of SSID, Supported rates, * FH Params, DS Params, CF Params, IBSS Params */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED probe_resp; struct { u8 category; @@ -527,7 +535,7 @@ struct ieee80211_mgmt { u8 action_code; u8 dialog_token; u8 status_code; - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED wmm_action; struct{ u8 action_code; @@ -541,14 +549,14 @@ struct ieee80211_mgmt { u8 action; u8 sta_addr[ETH_ALEN]; u8 target_ap_addr[ETH_ALEN]; - u8 variable[0]; /* FT Request */ + u8 variable[]; /* FT Request */ } STRUCT_PACKED ft_action_req; struct { u8 action; u8 sta_addr[ETH_ALEN]; u8 target_ap_addr[ETH_ALEN]; le16 status_code; - u8 variable[0]; /* FT Request */ + u8 variable[]; /* FT Request */ } STRUCT_PACKED ft_action_resp; struct { u8 action; @@ -561,23 +569,23 @@ struct ieee80211_mgmt { struct { u8 action; u8 dialogtoken; - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED wnm_sleep_req; struct { u8 action; u8 dialogtoken; le16 keydata_len; - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED wnm_sleep_resp; struct { u8 action; - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED public_action; struct { u8 action; /* 9 */ u8 oui[3]; /* Vendor-specific content */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED vs_public_action; struct { u8 action; /* 7 */ @@ -589,7 +597,7 @@ struct ieee80211_mgmt { * Session Information URL (optional), * BSS Transition Candidate List * Entries */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED bss_tm_req; struct { u8 action; /* 8 */ @@ -599,7 +607,7 @@ struct ieee80211_mgmt { /* Target BSSID (optional), * BSS Transition Candidate List * Entries (optional) */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED bss_tm_resp; struct { u8 action; /* 6 */ @@ -607,12 +615,16 @@ struct ieee80211_mgmt { u8 query_reason; /* BSS Transition Candidate List * Entries (optional) */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED bss_tm_query; struct { u8 action; /* 15 */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED slf_prot_action; + struct { + u8 action; + u8 variable[]; + } STRUCT_PACKED fst_action; } u; } STRUCT_PACKED action; } u; @@ -1065,6 +1077,15 @@ enum p2p_attr_id { #define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6) #define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7) +/* P2PS Coordination Protocol Transport Bitmap */ +#define P2PS_FEATURE_CAPAB_UDP_TRANSPORT BIT(0) +#define P2PS_FEATURE_CAPAB_MAC_TRANSPORT BIT(1) + +struct p2ps_feature_capab { + u8 cpt; + u8 reserved; +} STRUCT_PACKED; + /* Invitation Flags */ #define P2P_INVITATION_FLAGS_TYPE BIT(0) @@ -1354,4 +1375,62 @@ struct rrm_link_measurement_report { u8 variable[0]; } STRUCT_PACKED; +#define SSID_MAX_LEN 32 + +/* IEEE Std 802.11ad-2012 - Multi-band element */ +struct multi_band_ie { + u8 eid; /* WLAN_EID_MULTI_BAND */ + u8 len; + u8 mb_ctrl; + u8 band_id; + u8 op_class; + u8 chan; + u8 bssid[ETH_ALEN]; + le16 beacon_int; + u8 tsf_offs[8]; + u8 mb_connection_capability; + u8 fst_session_tmout; + /* Optional: + * STA MAC Address + * Pairwise Cipher Suite Count + * Pairwise Cipher Suite List + */ + u8 variable[0]; +} STRUCT_PACKED; + +enum mb_ctrl_sta_role { + MB_STA_ROLE_AP = 0, + MB_STA_ROLE_TDLS_STA = 1, + MB_STA_ROLE_IBSS_STA = 2, + MB_STA_ROLE_PCP = 3, + MB_STA_ROLE_NON_PCP_NON_AP = 4 +}; + +#define MB_CTRL_ROLE_MASK (BIT(0) | BIT(1) | BIT(2)) +#define MB_CTRL_ROLE(ctrl) ((u8) ((ctrl) & MB_CTRL_ROLE_MASK)) +#define MB_CTRL_STA_MAC_PRESENT ((u8) (BIT(3))) +#define MB_CTRL_PAIRWISE_CIPHER_SUITE_PRESENT ((u8) (BIT(4))) + +enum mb_band_id { + MB_BAND_ID_WIFI_2_4GHZ = 2, /* 2.4 GHz */ + MB_BAND_ID_WIFI_5GHZ = 4, /* 4.9 and 5 GHz */ + MB_BAND_ID_WIFI_60GHZ = 5, /* 60 GHz */ +}; + +#define MB_CONNECTION_CAPABILITY_AP ((u8) (BIT(0))) +#define MB_CONNECTION_CAPABILITY_PCP ((u8) (BIT(1))) +#define MB_CONNECTION_CAPABILITY_DLS ((u8) (BIT(2))) +#define MB_CONNECTION_CAPABILITY_TDLS ((u8) (BIT(3))) +#define MB_CONNECTION_CAPABILITY_IBSS ((u8) (BIT(4))) + +/* IEEE Std 802.11ad-2014 - FST Action field */ +enum fst_action { + FST_ACTION_SETUP_REQUEST = 0, + FST_ACTION_SETUP_RESPONSE = 1, + FST_ACTION_TEAR_DOWN = 2, + FST_ACTION_ACK_REQUEST = 3, + FST_ACTION_ACK_RESPONSE = 4, + FST_ACTION_ON_CHANNEL_TUNNEL = 5, +}; + #endif /* IEEE802_11_DEFS_H */ diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h index 4dc34c4ad322b..8dff30382b605 100644 --- a/src/common/privsep_commands.h +++ b/src/common/privsep_commands.h @@ -9,6 +9,8 @@ #ifndef PRIVSEP_COMMANDS_H #define PRIVSEP_COMMANDS_H +#include "common/ieee802_11_defs.h" + enum privsep_cmd { PRIVSEP_CMD_REGISTER, PRIVSEP_CMD_UNREGISTER, @@ -24,12 +26,31 @@ enum privsep_cmd { PRIVSEP_CMD_L2_NOTIFY_AUTH_START, PRIVSEP_CMD_L2_SEND, PRIVSEP_CMD_SET_COUNTRY, + PRIVSEP_CMD_AUTHENTICATE, +}; + +struct privsep_cmd_authenticate +{ + int freq; + u8 bssid[ETH_ALEN]; + u8 ssid[SSID_MAX_LEN]; + size_t ssid_len; + int auth_alg; + size_t ie_len; + u8 wep_key[4][16]; + size_t wep_key_len[4]; + int wep_tx_keyidx; + int local_state_change; + int p2p; + size_t sae_data_len; + /* followed by ie_len bytes of ie */ + /* followed by sae_data_len bytes of sae_data */ }; struct privsep_cmd_associate { u8 bssid[ETH_ALEN]; - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; int hwmode; int freq; @@ -66,6 +87,18 @@ enum privsep_event { PRIVSEP_EVENT_STKSTART, PRIVSEP_EVENT_FT_RESPONSE, PRIVSEP_EVENT_RX_EAPOL, + PRIVSEP_EVENT_SCAN_STARTED, + PRIVSEP_EVENT_AUTH, +}; + +struct privsep_event_auth { + u8 peer[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + u16 auth_type; + u16 auth_transaction; + u16 status_code; + size_t ies_len; + /* followed by ies_len bytes of ies */ }; #endif /* PRIVSEP_COMMANDS_H */ diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h index 2117ee7028c1d..28985f5194e21 100644 --- a/src/common/qca-vendor.h +++ b/src/common/qca-vendor.h @@ -132,7 +132,7 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY = 50, QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH = 51, QCA_NL80211_VENDOR_SUBCMD_APFIND = 52, - /* 53 - reserved for QCA */ + /* 53 - reserved - was used by QCA, but not in use anymore */ QCA_NL80211_VENDOR_SUBCMD_DO_ACS = 54, QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES = 55, QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED = 56, @@ -142,6 +142,20 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED = 60, /* 61-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, + QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT = 94, + QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT = 95, + QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER = 96, + QCA_NL80211_VENDOR_SUBCMD_DCC_GET_STATS = 97, + QCA_NL80211_VENDOR_SUBCMD_DCC_CLEAR_STATS = 98, + QCA_NL80211_VENDOR_SUBCMD_DCC_UPDATE_NDL = 99, + QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT = 100, + QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES = 101, + QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG = 102, + QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST = 103, + QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL = 104, + QCA_NL80211_VENDOR_SUBCMD_SETBAND = 105, }; @@ -162,6 +176,15 @@ enum qca_wlan_vendor_attr { /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */ QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS = 7, QCA_WLAN_VENDOR_ATTR_TEST = 8, + /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */ + /* Unsigned 32-bit value. */ + QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA = 9, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND = 10, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND = 11, + /* Unsigned 32-bit value from enum qca_set_band. */ + QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE = 12, /* keep last */ QCA_WLAN_VENDOR_ATTR_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1, @@ -195,6 +218,12 @@ enum qca_wlan_vendor_attr_acs_offload { QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE, QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED, + QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED, + QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH, + QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST, + QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL, + QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL, + QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST, /* keep last */ QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ACS_MAX = @@ -206,6 +235,7 @@ enum qca_wlan_vendor_acs_hw_mode { QCA_ACS_MODE_IEEE80211G, QCA_ACS_MODE_IEEE80211A, QCA_ACS_MODE_IEEE80211AD, + QCA_ACS_MODE_IEEE80211ANY, }; /** @@ -215,10 +245,13 @@ enum qca_wlan_vendor_acs_hw_mode { * management offload, a mechanism where the station's firmware * does the exchange with the AP to establish the temporal keys * after roaming, rather than having the user space wpa_supplicant do it. + * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic + * band selection based on channel selection results. * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits */ enum qca_wlan_vendor_features { QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD = 0, + QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY = 1, NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */ }; @@ -243,4 +276,82 @@ enum qca_wlan_vendor_attr_data_offload_ind { QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_MAX = QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_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 + * frequency list is requested (see enum qca_iface_type for possible + * values); used in GET_PREFERRED_FREQ_LIST command from user-space to + * kernel and in the kernel response back to user-space. + */ + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE, + /* An array of 32-unsigned values; values are frequency (MHz); sent + * from kernel space to user space. + */ + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX = + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST - 1 +}; + +enum qca_vendor_attr_probable_oper_channel { + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_INVALID, + /* 32-bit unsigned value; indicates the connection/iface type likely to + * come on this channel (see enum qca_iface_type). + */ + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE, + /* 32-bit unsigned value; the frequency (MHz) of the probable channel */ + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX = + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST - 1 +}; + +enum qca_iface_type { + QCA_IFACE_TYPE_STA, + QCA_IFACE_TYPE_AP, + QCA_IFACE_TYPE_P2P_CLIENT, + QCA_IFACE_TYPE_P2P_GO, + QCA_IFACE_TYPE_IBSS, + QCA_IFACE_TYPE_TDLS, +}; + +enum qca_set_band { + QCA_SETBAND_AUTO, + QCA_SETBAND_5G, + QCA_SETBAND_2G, +}; + +/* IEEE 802.11 Vendor Specific elements */ + +/** + * enum qca_vendor_element_id - QCA Vendor Specific element types + * + * These values are used to identify QCA Vendor Specific elements. The + * payload of the element starts with the three octet OUI (OUI_QCA) and + * is followed by a single octet type which is defined by this enum. + * + * @QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST: P2P preferred channel list. + * This element can be used to specify preference order for supported + * channels. The channels in this list are in preference order (the first + * one has the highest preference) and are described as a pair of + * (global) Operating Class and Channel Number (each one octet) fields. + * + * This extends the standard P2P functionality by providing option to have + * more than one preferred operating channel. When this element is present, + * it replaces the preference indicated in the Operating Channel attribute. + * For supporting other implementations, the Operating Channel attribute is + * expected to be used with the highest preference channel. Similarly, all + * the channels included in this Preferred channel list element are + * expected to be included in the Channel List attribute. + * + * This vendor element may be included in GO Negotiation Request, P2P + * Invitation Request, and Provision Discovery Request frames. + */ +enum qca_vendor_element_id { + QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0, +}; + #endif /* QCA_VENDOR_H */ diff --git a/src/common/sae.c b/src/common/sae.c index 588895808fde2..503fa1d7b9a96 100644 --- a/src/common/sae.c +++ b/src/common/sae.c @@ -1,6 +1,6 @@ /* * Simultaneous authentication of equals - * Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2012-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -124,9 +124,7 @@ static struct crypto_bignum * sae_get_rand(struct sae_data *sae) return NULL; for (;;) { - if (iter++ > 100) - return NULL; - if (random_get_bytes(val, order_len) < 0) + if (iter++ > 100 || random_get_bytes(val, order_len) < 0) return NULL; if (order_len_bits % 8) buf_shift_right(val, order_len, 8 - order_len_bits % 8); @@ -171,17 +169,107 @@ static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key) } +static struct crypto_bignum * +get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits, + int *r_odd) +{ + for (;;) { + struct crypto_bignum *r; + u8 tmp[SAE_MAX_ECC_PRIME_LEN]; + + if (random_get_bytes(tmp, prime_len) < 0) + break; + if (prime_bits % 8) + buf_shift_right(tmp, prime_len, 8 - prime_bits % 8); + if (os_memcmp(tmp, prime, prime_len) >= 0) + continue; + r = crypto_bignum_init_set(tmp, prime_len); + if (!r) + break; + if (crypto_bignum_is_zero(r)) { + crypto_bignum_deinit(r, 0); + continue; + } + + *r_odd = tmp[prime_len - 1] & 0x01; + return r; + } + + return NULL; +} + + +static int is_quadratic_residue_blind(struct sae_data *sae, + const u8 *prime, size_t bits, + const struct crypto_bignum *qr, + const struct crypto_bignum *qnr, + const struct crypto_bignum *y_sqr) +{ + struct crypto_bignum *r, *num; + int r_odd, check, res = -1; + + /* + * Use the blinding technique to mask y_sqr while determining + * whether it is a quadratic residue modulo p to avoid leaking + * timing information while determining the Legendre symbol. + * + * v = y_sqr + * r = a random number between 1 and p-1, inclusive + * num = (v * r * r) modulo p + */ + r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd); + if (!r) + return -1; + + num = crypto_bignum_init(); + if (!num || + crypto_bignum_mulmod(y_sqr, r, sae->tmp->prime, num) < 0 || + crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0) + goto fail; + + if (r_odd) { + /* + * num = (num * qr) module p + * LGR(num, p) = 1 ==> quadratic residue + */ + if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0) + goto fail; + check = 1; + } else { + /* + * num = (num * qnr) module p + * LGR(num, p) = -1 ==> quadratic residue + */ + if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0) + goto fail; + check = -1; + } + + res = crypto_bignum_legendre(num, sae->tmp->prime); + if (res == -2) { + res = -1; + goto fail; + } + res = res == check; +fail: + crypto_bignum_deinit(num, 1); + crypto_bignum_deinit(r, 1); + return res; +} + + static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, - struct crypto_ec_point *pwe) + const u8 *prime, + const struct crypto_bignum *qr, + const struct crypto_bignum *qnr, + struct crypto_bignum **ret_x_cand) { - u8 pwd_value[SAE_MAX_ECC_PRIME_LEN], prime[SAE_MAX_ECC_PRIME_LEN]; - struct crypto_bignum *x; - int y_bit; + u8 pwd_value[SAE_MAX_ECC_PRIME_LEN]; + struct crypto_bignum *y_sqr, *x_cand; + int res; size_t bits; - if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), - sae->tmp->prime_len) < 0) - return -1; + *ret_x_cand = NULL; wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); @@ -197,20 +285,23 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0) return 0; - y_bit = pwd_seed[SHA256_MAC_LEN - 1] & 0x01; - - x = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); - if (x == NULL) + x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); + if (!x_cand) + return -1; + y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand); + if (!y_sqr) { + crypto_bignum_deinit(x_cand, 1); return -1; - if (crypto_ec_point_solve_y_coord(sae->tmp->ec, pwe, x, y_bit) < 0) { - crypto_bignum_deinit(x, 0); - wpa_printf(MSG_DEBUG, "SAE: No solution found"); - return 0; } - crypto_bignum_deinit(x, 0); - wpa_printf(MSG_DEBUG, "SAE: PWE found"); + res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr); + crypto_bignum_deinit(y_sqr, 1); + if (res <= 0) { + crypto_bignum_deinit(x_cand, 1); + return res; + } + *ret_x_cand = x_cand; return 1; } @@ -288,24 +379,77 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, } +static int get_random_qr_qnr(const u8 *prime, size_t prime_len, + const struct crypto_bignum *prime_bn, + size_t prime_bits, struct crypto_bignum **qr, + struct crypto_bignum **qnr) +{ + *qr = NULL; + *qnr = NULL; + + while (!(*qr) || !(*qnr)) { + u8 tmp[SAE_MAX_ECC_PRIME_LEN]; + struct crypto_bignum *q; + int res; + + if (random_get_bytes(tmp, prime_len) < 0) + break; + if (prime_bits % 8) + buf_shift_right(tmp, prime_len, 8 - prime_bits % 8); + if (os_memcmp(tmp, prime, prime_len) >= 0) + continue; + q = crypto_bignum_init_set(tmp, prime_len); + if (!q) + break; + res = crypto_bignum_legendre(q, prime_bn); + + if (res == 1 && !(*qr)) + *qr = q; + else if (res == -1 && !(*qnr)) + *qnr = q; + else + crypto_bignum_deinit(q, 0); + } + + return (*qr && *qnr) ? 0 : -1; +} + + static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len) { - u8 counter, k = 4; + u8 counter, k = 40; u8 addrs[2 * ETH_ALEN]; const u8 *addr[2]; size_t len[2]; - int found = 0; - struct crypto_ec_point *pwe_tmp; + u8 dummy_password[32]; + size_t dummy_password_len; + int pwd_seed_odd = 0; + u8 prime[SAE_MAX_ECC_PRIME_LEN]; + size_t prime_len; + struct crypto_bignum *x = NULL, *qr, *qnr; + size_t bits; + int res; - if (sae->tmp->pwe_ecc == NULL) { - sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); - if (sae->tmp->pwe_ecc == NULL) - return -1; - } - pwe_tmp = crypto_ec_point_init(sae->tmp->ec); - if (pwe_tmp == NULL) + dummy_password_len = password_len; + if (dummy_password_len > sizeof(dummy_password)) + dummy_password_len = sizeof(dummy_password); + if (random_get_bytes(dummy_password, dummy_password_len) < 0) + return -1; + + prime_len = sae->tmp->prime_len; + if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), + prime_len) < 0) + return -1; + bits = crypto_ec_prime_len_bits(sae->tmp->ec); + + /* + * Create a random quadratic residue (qr) and quadratic non-residue + * (qnr) modulo p for blinding purposes during the loop. + */ + if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits, + &qr, &qnr) < 0) return -1; wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", @@ -313,8 +457,9 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, /* * H(salt, ikm) = HMAC-SHA256(salt, ikm) + * base = password * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), - * password || counter) + * base || counter) */ sae_pwd_seed_key(addr1, addr2, addrs); @@ -328,9 +473,9 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, * attacks that attempt to determine the number of iterations required * in the loop. */ - for (counter = 1; counter < k || !found; counter++) { + for (counter = 1; counter <= k || !x; counter++) { u8 pwd_seed[SHA256_MAC_LEN]; - int res; + struct crypto_bignum *x_cand; if (counter > 200) { /* This should not happen in practice */ @@ -342,25 +487,58 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, pwd_seed) < 0) break; + res = sae_test_pwd_seed_ecc(sae, pwd_seed, - found ? pwe_tmp : - sae->tmp->pwe_ecc); + prime, qr, qnr, &x_cand); if (res < 0) - break; - if (res == 0) - continue; - if (found) { - wpa_printf(MSG_DEBUG, "SAE: Ignore this PWE (one was " - "already selected)"); - } else { - wpa_printf(MSG_DEBUG, "SAE: Use this PWE"); - found = 1; + goto fail; + if (res > 0 && !x) { + wpa_printf(MSG_DEBUG, + "SAE: Selected pwd-seed with counter %u", + counter); + x = x_cand; + pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01; + os_memset(pwd_seed, 0, sizeof(pwd_seed)); + + /* + * Use a dummy password for the following rounds, if + * any. + */ + addr[0] = dummy_password; + len[0] = dummy_password_len; + } else if (res > 0) { + crypto_bignum_deinit(x_cand, 1); } } - crypto_ec_point_deinit(pwe_tmp, 1); + if (!x) { + wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); + res = -1; + goto fail; + } - return found ? 0 : -1; + if (!sae->tmp->pwe_ecc) + sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); + if (!sae->tmp->pwe_ecc) + res = -1; + else + res = crypto_ec_point_solve_y_coord(sae->tmp->ec, + sae->tmp->pwe_ecc, x, + pwd_seed_odd); + crypto_bignum_deinit(x, 1); + if (res < 0) { + /* + * This should not happen since we already checked that there + * is a result. + */ + wpa_printf(MSG_DEBUG, "SAE: Could not solve y"); + } + +fail: + crypto_bignum_deinit(qr, 0); + crypto_bignum_deinit(qnr, 0); + + return res; } @@ -472,27 +650,41 @@ static int sae_derive_commit(struct sae_data *sae) { struct crypto_bignum *mask; int ret = -1; + unsigned int counter = 0; + + do { + counter++; + if (counter > 100) { + /* + * This cannot really happen in practice if the random + * number generator is working. Anyway, to avoid even a + * theoretical infinite loop, break out after 100 + * attemps. + */ + return -1; + } - mask = sae_get_rand_and_mask(sae); - if (mask == NULL) { - wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask"); - return -1; - } - - /* commit-scalar = (rand + mask) modulo r */ - if (!sae->tmp->own_commit_scalar) { - sae->tmp->own_commit_scalar = crypto_bignum_init(); - if (!sae->tmp->own_commit_scalar) - goto fail; - } - crypto_bignum_add(sae->tmp->sae_rand, mask, - sae->tmp->own_commit_scalar); - crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order, - sae->tmp->own_commit_scalar); + mask = sae_get_rand_and_mask(sae); + if (mask == NULL) { + wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask"); + return -1; + } - if (sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) - goto fail; - if (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0) + /* commit-scalar = (rand + mask) modulo r */ + if (!sae->tmp->own_commit_scalar) { + sae->tmp->own_commit_scalar = crypto_bignum_init(); + if (!sae->tmp->own_commit_scalar) + goto fail; + } + crypto_bignum_add(sae->tmp->sae_rand, mask, + sae->tmp->own_commit_scalar); + crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order, + sae->tmp->own_commit_scalar); + } while (crypto_bignum_is_zero(sae->tmp->own_commit_scalar) || + crypto_bignum_is_one(sae->tmp->own_commit_scalar)); + + if ((sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) || + (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0)) goto fail; ret = 0; @@ -506,15 +698,12 @@ int sae_prepare_commit(const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len, struct sae_data *sae) { - if (sae->tmp == NULL) - return -1; - if (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, - password_len) < 0) - return -1; - if (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, - password_len) < 0) - return -1; - if (sae_derive_commit(sae) < 0) + if (sae->tmp == NULL || + (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, + password_len) < 0) || + (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, + password_len) < 0) || + sae_derive_commit(sae) < 0) return -1; return 0; } @@ -780,8 +969,9 @@ static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, return WLAN_STATUS_UNSPECIFIED_FAILURE; } - /* 0 < scalar < r */ + /* 1 < scalar < r */ if (crypto_bignum_is_zero(peer_scalar) || + crypto_bignum_is_one(peer_scalar) || crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) { wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar"); crypto_bignum_deinit(peer_scalar, 0); @@ -847,7 +1037,8 @@ static u16 sae_parse_commit_element_ecc(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; + struct crypto_bignum *res, *one; + const u8 one_bin[1] = { 0x01 }; if (pos + sae->tmp->prime_len > end) { wpa_printf(MSG_DEBUG, "SAE: Not enough data for " @@ -862,18 +1053,23 @@ static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, crypto_bignum_init_set(pos, sae->tmp->prime_len); if (sae->tmp->peer_commit_element_ffc == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; - if (crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || + /* 1 < element < p - 1 */ + res = crypto_bignum_init(); + one = crypto_bignum_init_set(one_bin, sizeof(one_bin)); + if (!res || !one || + crypto_bignum_sub(sae->tmp->prime, one, res) || + crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) || - crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, - sae->tmp->prime) >= 0) { + crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, res) >= 0) { + crypto_bignum_deinit(res, 0); + crypto_bignum_deinit(one, 0); wpa_printf(MSG_DEBUG, "SAE: Invalid peer element"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } + crypto_bignum_deinit(one, 0); /* scalar-op(r, ELEMENT) = 1 modulo p */ - res = crypto_bignum_init(); - if (res == NULL || - crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, + if (crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, sae->tmp->order, sae->tmp->prime, res) < 0 || !crypto_bignum_is_one(res)) { wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)"); @@ -918,7 +1114,34 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, return res; /* commit-element */ - return sae_parse_commit_element(sae, pos, end); + res = sae_parse_commit_element(sae, pos, end); + if (res != WLAN_STATUS_SUCCESS) + return res; + + /* + * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as + * the values we sent which would be evidence of a reflection attack. + */ + if (!sae->tmp->own_commit_scalar || + crypto_bignum_cmp(sae->tmp->own_commit_scalar, + sae->peer_commit_scalar) != 0 || + (sae->tmp->dh && + (!sae->tmp->own_commit_element_ffc || + crypto_bignum_cmp(sae->tmp->own_commit_element_ffc, + sae->tmp->peer_commit_element_ffc) != 0)) || + (sae->tmp->ec && + (!sae->tmp->own_commit_element_ecc || + crypto_ec_point_cmp(sae->tmp->ec, + sae->tmp->own_commit_element_ecc, + sae->tmp->peer_commit_element_ecc) != 0))) + return WLAN_STATUS_SUCCESS; /* scalars/elements are different */ + + /* + * This is a reflection attack - return special value to trigger caller + * to silently discard the frame instead of replying with a specific + * status code. + */ + return SAE_SILENTLY_DISCARD; } diff --git a/src/common/sae.h b/src/common/sae.h index 3ebf40cf4a45d..c07026cd497cc 100644 --- a/src/common/sae.h +++ b/src/common/sae.h @@ -18,6 +18,9 @@ #define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN) #define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN) +/* Special value returned by sae_parse_commit() */ +#define SAE_SILENTLY_DISCARD 65535 + struct sae_temporary_data { u8 kck[SAE_KCK_LEN]; struct crypto_bignum *own_commit_scalar; diff --git a/src/common/version.h b/src/common/version.h index e39a8dbf92e79..a5cc5b7b5bccb 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -5,6 +5,6 @@ #define VERSION_STR_POSTFIX "" #endif /* VERSION_STR_POSTFIX */ -#define VERSION_STR "2.4" VERSION_STR_POSTFIX +#define VERSION_STR "2.5" VERSION_STR_POSTFIX #endif /* VERSION_H */ diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index de81d53694c29..e9d4248d72d45 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -170,6 +170,12 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, ptk->tk_len = wpa_cipher_key_len(cipher); 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), @@ -207,8 +213,10 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, const u8 *rsnie, size_t rsnie_len, const u8 *ric, size_t ric_len, u8 *mic) { - u8 *buf, *pos; - size_t buf_len; + const u8 *addr[9]; + size_t len[9]; + size_t i, num_elem = 0; + u8 zero_mic[16]; if (kck_len != 16) { wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u", @@ -216,48 +224,58 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, return -1; } - buf_len = 2 * ETH_ALEN + 1 + mdie_len + ftie_len + rsnie_len + ric_len; - buf = os_malloc(buf_len); - if (buf == NULL) - return -1; + addr[num_elem] = sta_addr; + len[num_elem] = ETH_ALEN; + num_elem++; + + addr[num_elem] = ap_addr; + len[num_elem] = ETH_ALEN; + num_elem++; + + addr[num_elem] = &transaction_seqnum; + len[num_elem] = 1; + num_elem++; - pos = buf; - os_memcpy(pos, sta_addr, ETH_ALEN); - pos += ETH_ALEN; - os_memcpy(pos, ap_addr, ETH_ALEN); - pos += ETH_ALEN; - *pos++ = transaction_seqnum; if (rsnie) { - os_memcpy(pos, rsnie, rsnie_len); - pos += rsnie_len; + addr[num_elem] = rsnie; + len[num_elem] = rsnie_len; + num_elem++; } if (mdie) { - os_memcpy(pos, mdie, mdie_len); - pos += mdie_len; + addr[num_elem] = mdie; + len[num_elem] = mdie_len; + num_elem++; } if (ftie) { - struct rsn_ftie *_ftie; - os_memcpy(pos, ftie, ftie_len); - if (ftie_len < 2 + sizeof(*_ftie)) { - os_free(buf); + if (ftie_len < 2 + sizeof(struct rsn_ftie)) return -1; - } - _ftie = (struct rsn_ftie *) (pos + 2); - os_memset(_ftie->mic, 0, sizeof(_ftie->mic)); - pos += ftie_len; + + /* IE hdr and mic_control */ + addr[num_elem] = ftie; + len[num_elem] = 2 + 2; + num_elem++; + + /* MIC field with all zeros */ + os_memset(zero_mic, 0, sizeof(zero_mic)); + addr[num_elem] = zero_mic; + len[num_elem] = sizeof(zero_mic); + num_elem++; + + /* Rest of FTIE */ + addr[num_elem] = ftie + 2 + 2 + 16; + len[num_elem] = ftie_len - (2 + 2 + 16); + num_elem++; } if (ric) { - os_memcpy(pos, ric, ric_len); - pos += ric_len; + addr[num_elem] = ric; + len[num_elem] = ric_len; + num_elem++; } - wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", buf, pos - buf); - if (omac1_aes_128(kck, buf, pos - buf, mic)) { - os_free(buf); + 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)) return -1; - } - - os_free(buf); return 0; } @@ -344,6 +362,8 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, parse->rsn_pmkid = data.pmkid; break; case WLAN_EID_MOBILITY_DOMAIN: + if (pos[1] < sizeof(struct rsn_mdie)) + return -1; parse->mdie = pos + 2; parse->mdie_len = pos[1]; break; @@ -356,6 +376,8 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, return -1; break; case WLAN_EID_TIMEOUT_INTERVAL: + if (pos[1] != 5) + break; parse->tie = pos + 2; parse->tie_len = pos[1]; break; @@ -416,14 +438,10 @@ static int rsn_selector_to_bitfield(const u8 *s) { if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE) return WPA_CIPHER_NONE; - if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP40) - return WPA_CIPHER_WEP40; if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_TKIP) return WPA_CIPHER_TKIP; if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP) return WPA_CIPHER_CCMP; - if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP104) - return WPA_CIPHER_WEP104; #ifdef CONFIG_IEEE80211W if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC) return WPA_CIPHER_AES_128_CMAC; @@ -474,15 +492,15 @@ 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_OSEN) + return WPA_KEY_MGMT_OSEN; return 0; } -static int wpa_cipher_valid_group(int cipher) +int wpa_cipher_valid_group(int cipher) { return wpa_cipher_valid_pairwise(cipher) || - cipher == WPA_CIPHER_WEP104 || - cipher == WPA_CIPHER_WEP40 || cipher == WPA_CIPHER_GTK_NOT_USED; } @@ -508,7 +526,6 @@ int wpa_cipher_valid_mgmt_group(int cipher) int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data) { - const struct rsn_ie_hdr *hdr; const u8 *pos; int left; int i, count; @@ -538,18 +555,29 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, return -1; } - hdr = (const struct rsn_ie_hdr *) rsn_ie; + if (rsn_ie_len >= 6 && rsn_ie[1] >= 4 && + rsn_ie[1] == rsn_ie_len - 2 && + WPA_GET_BE32(&rsn_ie[2]) == OSEN_IE_VENDOR_TYPE) { + pos = rsn_ie + 6; + left = rsn_ie_len - 6; - if (hdr->elem_id != WLAN_EID_RSN || - hdr->len != rsn_ie_len - 2 || - WPA_GET_LE16(hdr->version) != RSN_VERSION) { - wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", - __func__); - return -2; - } + data->proto = WPA_PROTO_OSEN; + } else { + const struct rsn_ie_hdr *hdr; - pos = (const u8 *) (hdr + 1); - left = rsn_ie_len - sizeof(*hdr); + hdr = (const struct rsn_ie_hdr *) rsn_ie; + + if (hdr->elem_id != WLAN_EID_RSN || + hdr->len != rsn_ie_len - 2 || + WPA_GET_LE16(hdr->version) != RSN_VERSION) { + wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", + __func__); + return -2; + } + + pos = (const u8 *) (hdr + 1); + left = rsn_ie_len - sizeof(*hdr); + } if (left >= RSN_SELECTOR_LEN) { data->group_cipher = rsn_selector_to_bitfield(pos); @@ -667,14 +695,10 @@ static int wpa_selector_to_bitfield(const u8 *s) { if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE) return WPA_CIPHER_NONE; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40) - return WPA_CIPHER_WEP40; if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP) return WPA_CIPHER_TKIP; if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP) return WPA_CIPHER_CCMP; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104) - return WPA_CIPHER_WEP104; return 0; } @@ -709,11 +733,6 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, data->num_pmkid = 0; data->mgmt_group_cipher = 0; - if (wpa_ie_len == 0) { - /* No WPA IE - fail silently */ - return -1; - } - if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) { wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", __func__, (unsigned long) wpa_ie_len); @@ -814,7 +833,7 @@ void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name) { - u8 buf[1 + WPA_MAX_SSID_LEN + MOBILITY_DOMAIN_ID_LEN + 1 + + 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]; const u8 *addr[2]; @@ -828,7 +847,7 @@ void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, * PMK-R0 = L(R0-Key-Data, 0, 256) * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128) */ - if (ssid_len > WPA_MAX_SSID_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN) + if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN) return; pos = buf; *pos++ = ssid_len; @@ -1279,6 +1298,9 @@ int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) os_memmove(rpos + 2, rpos, end - rpos); *rpos++ = 0; *rpos++ = 0; + added += 2; + start[1] += 2; + rend = rpos; } else { /* Skip RSN Capabilities */ rpos += 2; @@ -1291,7 +1313,7 @@ int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) if (rpos == rend) { /* No PMKID-Count field included; add it */ - os_memmove(rpos + 2 + PMKID_LEN, rpos, end - rpos); + os_memmove(rpos + 2 + PMKID_LEN, rpos, end + added - rpos); WPA_PUT_LE16(rpos, 1); rpos += 2; os_memcpy(rpos, pmkid, PMKID_LEN); @@ -1306,7 +1328,7 @@ int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) } WPA_PUT_LE16(rpos, 1); rpos += 2; - os_memmove(rpos + PMKID_LEN, rpos, end - rpos); + os_memmove(rpos + PMKID_LEN, rpos, end + added - rpos); os_memcpy(rpos, pmkid, PMKID_LEN); added += PMKID_LEN; start[1] += PMKID_LEN; @@ -1335,10 +1357,6 @@ int wpa_cipher_key_len(int cipher) return 16; case WPA_CIPHER_TKIP: return 32; - case WPA_CIPHER_WEP104: - return 13; - case WPA_CIPHER_WEP40: - return 5; } return 0; @@ -1354,9 +1372,6 @@ int wpa_cipher_rsc_len(int cipher) case WPA_CIPHER_GCMP: case WPA_CIPHER_TKIP: return 6; - case WPA_CIPHER_WEP104: - case WPA_CIPHER_WEP40: - return 0; } return 0; @@ -1376,9 +1391,6 @@ int wpa_cipher_to_alg(int cipher) return WPA_ALG_GCMP; case WPA_CIPHER_TKIP: return WPA_ALG_TKIP; - case WPA_CIPHER_WEP104: - case WPA_CIPHER_WEP40: - return WPA_ALG_WEP; case WPA_CIPHER_AES_128_CMAC: return WPA_ALG_IGTK; case WPA_CIPHER_BIP_GMAC_128: @@ -1416,12 +1428,6 @@ u32 wpa_cipher_to_suite(int proto, int cipher) if (cipher & WPA_CIPHER_TKIP) return (proto == WPA_PROTO_RSN ? RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP); - if (cipher & WPA_CIPHER_WEP104) - return (proto == WPA_PROTO_RSN ? - RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104); - if (cipher & WPA_CIPHER_WEP40) - return (proto == WPA_PROTO_RSN ? - RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40); if (cipher & WPA_CIPHER_NONE) return (proto == WPA_PROTO_RSN ? RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE); @@ -1525,10 +1531,6 @@ int wpa_pick_group_cipher(int ciphers) return WPA_CIPHER_GTK_NOT_USED; if (ciphers & WPA_CIPHER_TKIP) return WPA_CIPHER_TKIP; - if (ciphers & WPA_CIPHER_WEP104) - return WPA_CIPHER_WEP104; - if (ciphers & WPA_CIPHER_WEP40) - return WPA_CIPHER_WEP40; return -1; } @@ -1626,20 +1628,6 @@ int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim) return -1; pos += ret; } - if (ciphers & WPA_CIPHER_WEP104) { - ret = os_snprintf(pos, end - pos, "%sWEP104", - pos == start ? "" : delim); - if (os_snprintf_error(end - pos, ret)) - return -1; - pos += ret; - } - if (ciphers & WPA_CIPHER_WEP40) { - ret = os_snprintf(pos, end - pos, "%sWEP40", - 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); diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 091e317fdd68a..c08f6514ab573 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -9,8 +9,6 @@ #ifndef WPA_COMMON_H #define WPA_COMMON_H -#define WPA_MAX_SSID_LEN 32 - /* IEEE 802.11i */ #define PMKID_LEN 16 #define PMK_LEN 32 @@ -24,8 +22,8 @@ (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \ WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256) #define WPA_ALLOWED_GROUP_CIPHERS \ -(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | \ -WPA_CIPHER_WEP40 | 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_SELECTOR_LEN 4 @@ -42,13 +40,8 @@ WPA_CIPHER_GTK_NOT_USED) #define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2) #define WPA_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0) #define WPA_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0) -#define WPA_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x50, 0xf2, 1) #define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2) -#if 0 -#define WPA_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x50, 0xf2, 3) -#endif #define WPA_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x50, 0xf2, 4) -#define WPA_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x50, 0xf2, 5) #define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1) @@ -70,13 +63,11 @@ RSN_SELECTOR(0x00, 0x0f, 0xac, 13) #define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01) #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) @@ -308,7 +299,6 @@ struct wpa_igtk_kde { } STRUCT_PACKED; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R struct rsn_mdie { u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 ft_capab; @@ -336,7 +326,6 @@ struct rsn_rdie { le16 status_code; } STRUCT_PACKED; -#endif /* CONFIG_IEEE80211R */ #ifdef _MSC_VER #pragma pack(pop) @@ -446,6 +435,7 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse); int wpa_cipher_key_len(int cipher); int wpa_cipher_rsc_len(int cipher); int 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); u32 wpa_cipher_to_suite(int proto, int cipher); diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c index ccaaf1b056b58..5733aa605d18c 100644 --- a/src/common/wpa_ctrl.c +++ b/src/common/wpa_ctrl.c @@ -21,6 +21,7 @@ #ifdef ANDROID #include <dirent.h> +#include <sys/stat.h> #include <cutils/sockets.h> #include "private/android_filesystem_config.h" #endif /* ANDROID */ @@ -84,6 +85,13 @@ struct wpa_ctrl { struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) { + return wpa_ctrl_open2(ctrl_path, NULL); +} + + +struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, + const char *cli_path) +{ struct wpa_ctrl *ctrl; static int counter = 0; int ret; @@ -107,10 +115,18 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) ctrl->local.sun_family = AF_UNIX; counter++; try_again: - ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), - CONFIG_CTRL_IFACE_CLIENT_DIR "/" - CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", - (int) getpid(), counter); + if (cli_path && cli_path[0] == '/') { + ret = os_snprintf(ctrl->local.sun_path, + sizeof(ctrl->local.sun_path), + "%s/" CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", + cli_path, (int) getpid(), counter); + } else { + ret = os_snprintf(ctrl->local.sun_path, + sizeof(ctrl->local.sun_path), + CONFIG_CTRL_IFACE_CLIENT_DIR "/" + CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", + (int) getpid(), counter); + } if (os_snprintf_error(sizeof(ctrl->local.sun_path), ret)) { close(ctrl->s); os_free(ctrl); @@ -136,6 +152,8 @@ try_again: #ifdef ANDROID chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + /* Set group even if we do not have privileges to change owner */ + chown(ctrl->local.sun_path, -1, AID_WIFI); chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI); if (os_strncmp(ctrl_path, "@android:", 9) == 0) { diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 1d19fc550df65..3de46823588b8 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -28,6 +28,8 @@ extern "C" { #define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED " /** Association rejected during connection attempt */ #define WPA_EVENT_ASSOC_REJECT "CTRL-EVENT-ASSOC-REJECT " +/** Authentication rejected during connection attempt */ +#define WPA_EVENT_AUTH_REJECT "CTRL-EVENT-AUTH-REJECT " /** wpa_supplicant is exiting */ #define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING " /** Password change was completed successfully */ @@ -68,6 +70,8 @@ extern "C" { #define WPA_EVENT_BSS_ADDED "CTRL-EVENT-BSS-ADDED " /** A BSS entry was removed (followed by BSS entry id and BSSID) */ #define WPA_EVENT_BSS_REMOVED "CTRL-EVENT-BSS-REMOVED " +/** No suitable network was found */ +#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 " /** Regulatory domain channel */ @@ -227,6 +231,7 @@ extern "C" { #define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED " #define AP_STA_CONNECTED "AP-STA-CONNECTED " #define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED " +#define AP_STA_POSSIBLE_PSK_MISMATCH "AP-STA-POSSIBLE-PSK-MISMATCH " #define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA " #define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA " @@ -276,6 +281,7 @@ extern "C" { #define WPA_BSS_MASK_MESH_SCAN BIT(18) #define WPA_BSS_MASK_SNR BIT(19) #define WPA_BSS_MASK_EST_THROUGHPUT BIT(20) +#define WPA_BSS_MASK_FST BIT(21) /* VENDOR_ELEM_* frame id values */ @@ -312,6 +318,20 @@ enum wpa_vendor_elem_frame { */ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path); +/** + * wpa_ctrl_open2 - Open a control interface to wpa_supplicant/hostapd + * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used. + * @cli_path: Path for client UNIX domain sockets; ignored if UDP socket + * is used. + * Returns: Pointer to abstract control interface data or %NULL on failure + * + * This function is used to open a control interface to wpa_supplicant/hostapd + * when the socket path for client need to be specified explicitly. Default + * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd and client + * socket path is /tmp. + */ +struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, const char *cli_path); + /** * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index f2d5662ff01e2..534c4bd786543 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -614,6 +614,15 @@ int crypto_bignum_is_zero(const struct crypto_bignum *a); int crypto_bignum_is_one(const struct crypto_bignum *a); /** + * crypto_bignum_legendre - Compute the Legendre symbol (a/p) + * @a: Bignum + * @p: Bignum + * Returns: Legendre symbol -1,0,1 on success; -2 on calculation failure + */ +int crypto_bignum_legendre(const struct crypto_bignum *a, + const struct crypto_bignum *p); + +/** * struct crypto_ec - Elliptic curve context * * Internal data structure for EC implementation. The contents is specific @@ -758,6 +767,16 @@ int crypto_ec_point_solve_y_coord(struct crypto_ec *e, const struct crypto_bignum *x, int y_bit); /** + * crypto_ec_point_compute_y_sqr - Compute y^2 = x^3 + ax + b + * @e: EC context from crypto_ec_init() + * @x: x coordinate + * Returns: y^2 on success, %NULL failure + */ +struct crypto_bignum * +crypto_ec_point_compute_y_sqr(struct crypto_ec *e, + const struct crypto_bignum *x); + +/** * crypto_ec_point_is_at_infinity - Check whether EC point is neutral element * @e: EC context from crypto_ec_init() * @p: EC point @@ -776,4 +795,15 @@ int crypto_ec_point_is_at_infinity(struct crypto_ec *e, int crypto_ec_point_is_on_curve(struct crypto_ec *e, const struct crypto_ec_point *p); +/** + * crypto_ec_point_cmp - Compare two EC points + * @e: EC context from crypto_ec_init() + * @a: EC point + * @b: EC point + * Returns: 0 on equal, non-zero otherwise + */ +int crypto_ec_point_cmp(const struct crypto_ec *e, + const struct crypto_ec_point *a, + const struct crypto_ec_point *b); + #endif /* CRYPTO_H */ diff --git a/src/crypto/crypto_cryptoapi.c b/src/crypto/crypto_cryptoapi.c deleted file mode 100644 index 55a069b0d036e..0000000000000 --- a/src/crypto/crypto_cryptoapi.c +++ /dev/null @@ -1,783 +0,0 @@ -/* - * Crypto wrapper for Microsoft CryptoAPI - * Copyright (c) 2005-2009, 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 <windows.h> -#include <wincrypt.h> - -#include "common.h" -#include "crypto.h" - -#ifndef MS_ENH_RSA_AES_PROV -#ifdef UNICODE -#define MS_ENH_RSA_AES_PROV \ -L"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" -#else -#define MS_ENH_RSA_AES_PROV \ -"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" -#endif -#endif /* MS_ENH_RSA_AES_PROV */ - -#ifndef CALG_HMAC -#define CALG_HMAC (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HMAC) -#endif - -#ifdef __MINGW32_VERSION -/* - * MinGW does not yet include all the needed definitions for CryptoAPI, so - * define here whatever extra is needed. - */ - -static BOOL WINAPI -(*CryptImportPublicKeyInfo)(HCRYPTPROV hCryptProv, DWORD dwCertEncodingType, - PCERT_PUBLIC_KEY_INFO pInfo, HCRYPTKEY *phKey) -= NULL; /* to be loaded from crypt32.dll */ - - -static int mingw_load_crypto_func(void) -{ - HINSTANCE dll; - - /* MinGW does not yet have full CryptoAPI support, so load the needed - * function here. */ - - if (CryptImportPublicKeyInfo) - return 0; - - dll = LoadLibrary("crypt32"); - if (dll == NULL) { - wpa_printf(MSG_DEBUG, "CryptoAPI: Could not load crypt32 " - "library"); - return -1; - } - - CryptImportPublicKeyInfo = GetProcAddress( - dll, "CryptImportPublicKeyInfo"); - if (CryptImportPublicKeyInfo == NULL) { - wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " - "CryptImportPublicKeyInfo() address from " - "crypt32 library"); - return -1; - } - - return 0; -} - -#else /* __MINGW32_VERSION */ - -static int mingw_load_crypto_func(void) -{ - return 0; -} - -#endif /* __MINGW32_VERSION */ - - -static void cryptoapi_report_error(const char *msg) -{ - char *s, *pos; - DWORD err = GetLastError(); - - if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM, - NULL, err, 0, (LPTSTR) &s, 0, NULL) == 0) { - wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d", msg, (int) err); - } - - pos = s; - while (*pos) { - if (*pos == '\n' || *pos == '\r') { - *pos = '\0'; - break; - } - pos++; - } - - wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d: (%s)", msg, (int) err, s); - LocalFree(s); -} - - -int cryptoapi_hash_vector(ALG_ID alg, size_t hash_len, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac) -{ - HCRYPTPROV prov; - HCRYPTHASH hash; - size_t i; - DWORD hlen; - int ret = 0; - - if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, 0)) { - cryptoapi_report_error("CryptAcquireContext"); - return -1; - } - - if (!CryptCreateHash(prov, alg, 0, 0, &hash)) { - cryptoapi_report_error("CryptCreateHash"); - CryptReleaseContext(prov, 0); - return -1; - } - - for (i = 0; i < num_elem; i++) { - if (!CryptHashData(hash, (BYTE *) addr[i], len[i], 0)) { - cryptoapi_report_error("CryptHashData"); - CryptDestroyHash(hash); - CryptReleaseContext(prov, 0); - } - } - - hlen = hash_len; - if (!CryptGetHashParam(hash, HP_HASHVAL, mac, &hlen, 0)) { - cryptoapi_report_error("CryptGetHashParam"); - ret = -1; - } - - CryptDestroyHash(hash); - CryptReleaseContext(prov, 0); - - return ret; -} - - -int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) -{ - return cryptoapi_hash_vector(CALG_MD4, 16, num_elem, addr, len, mac); -} - - -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) -{ - u8 next, tmp; - int i; - HCRYPTPROV prov; - HCRYPTKEY ckey; - DWORD dlen; - struct { - BLOBHEADER hdr; - DWORD len; - BYTE key[8]; - } key_blob; - DWORD mode = CRYPT_MODE_ECB; - - key_blob.hdr.bType = PLAINTEXTKEYBLOB; - key_blob.hdr.bVersion = CUR_BLOB_VERSION; - key_blob.hdr.reserved = 0; - key_blob.hdr.aiKeyAlg = CALG_DES; - key_blob.len = 8; - - /* Add parity bits to the key */ - next = 0; - for (i = 0; i < 7; i++) { - tmp = key[i]; - key_blob.key[i] = (tmp >> i) | next | 1; - next = tmp << (7 - i); - } - key_blob.key[i] = next | 1; - - if (!CryptAcquireContext(&prov, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: " - "%d", (int) GetLastError()); - return; - } - - if (!CryptImportKey(prov, (BYTE *) &key_blob, sizeof(key_blob), 0, 0, - &ckey)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d", - (int) GetLastError()); - CryptReleaseContext(prov, 0); - return; - } - - if (!CryptSetKeyParam(ckey, KP_MODE, (BYTE *) &mode, 0)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) " - "failed: %d", (int) GetLastError()); - CryptDestroyKey(ckey); - CryptReleaseContext(prov, 0); - return; - } - - os_memcpy(cypher, clear, 8); - dlen = 8; - if (!CryptEncrypt(ckey, 0, FALSE, 0, cypher, &dlen, 8)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d", - (int) GetLastError()); - os_memset(cypher, 0, 8); - } - - CryptDestroyKey(ckey); - CryptReleaseContext(prov, 0); -} - - -int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) -{ - return cryptoapi_hash_vector(CALG_MD5, 16, num_elem, addr, len, mac); -} - - -int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) -{ - return cryptoapi_hash_vector(CALG_SHA, 20, num_elem, addr, len, mac); -} - - -struct aes_context { - HCRYPTPROV prov; - HCRYPTKEY ckey; -}; - - -void * aes_encrypt_init(const u8 *key, size_t len) -{ - struct aes_context *akey; - struct { - BLOBHEADER hdr; - DWORD len; - BYTE key[16]; - } key_blob; - DWORD mode = CRYPT_MODE_ECB; - - if (len != 16) - return NULL; - - key_blob.hdr.bType = PLAINTEXTKEYBLOB; - key_blob.hdr.bVersion = CUR_BLOB_VERSION; - key_blob.hdr.reserved = 0; - key_blob.hdr.aiKeyAlg = CALG_AES_128; - key_blob.len = len; - os_memcpy(key_blob.key, key, len); - - akey = os_zalloc(sizeof(*akey)); - if (akey == NULL) - return NULL; - - if (!CryptAcquireContext(&akey->prov, NULL, - MS_ENH_RSA_AES_PROV, PROV_RSA_AES, - CRYPT_VERIFYCONTEXT)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: " - "%d", (int) GetLastError()); - os_free(akey); - return NULL; - } - - if (!CryptImportKey(akey->prov, (BYTE *) &key_blob, sizeof(key_blob), - 0, 0, &akey->ckey)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d", - (int) GetLastError()); - CryptReleaseContext(akey->prov, 0); - os_free(akey); - return NULL; - } - - if (!CryptSetKeyParam(akey->ckey, KP_MODE, (BYTE *) &mode, 0)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) " - "failed: %d", (int) GetLastError()); - CryptDestroyKey(akey->ckey); - CryptReleaseContext(akey->prov, 0); - os_free(akey); - return NULL; - } - - return akey; -} - - -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) -{ - struct aes_context *akey = ctx; - DWORD dlen; - - os_memcpy(crypt, plain, 16); - dlen = 16; - if (!CryptEncrypt(akey->ckey, 0, FALSE, 0, crypt, &dlen, 16)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d", - (int) GetLastError()); - os_memset(crypt, 0, 16); - } -} - - -void aes_encrypt_deinit(void *ctx) -{ - struct aes_context *akey = ctx; - if (akey) { - CryptDestroyKey(akey->ckey); - CryptReleaseContext(akey->prov, 0); - os_free(akey); - } -} - - -void * aes_decrypt_init(const u8 *key, size_t len) -{ - return aes_encrypt_init(key, len); -} - - -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) -{ - struct aes_context *akey = ctx; - DWORD dlen; - - os_memcpy(plain, crypt, 16); - dlen = 16; - - if (!CryptDecrypt(akey->ckey, 0, FALSE, 0, plain, &dlen)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptDecrypt failed: %d", - (int) GetLastError()); - } -} - - -void aes_decrypt_deinit(void *ctx) -{ - aes_encrypt_deinit(ctx); -} - - -struct crypto_hash { - enum crypto_hash_alg alg; - int error; - HCRYPTPROV prov; - HCRYPTHASH hash; - HCRYPTKEY key; -}; - -struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, - size_t key_len) -{ - struct crypto_hash *ctx; - ALG_ID calg; - struct { - BLOBHEADER hdr; - DWORD len; - BYTE key[32]; - } key_blob; - - os_memset(&key_blob, 0, sizeof(key_blob)); - switch (alg) { - case CRYPTO_HASH_ALG_MD5: - calg = CALG_MD5; - break; - case CRYPTO_HASH_ALG_SHA1: - calg = CALG_SHA; - break; - case CRYPTO_HASH_ALG_HMAC_MD5: - case CRYPTO_HASH_ALG_HMAC_SHA1: - calg = CALG_HMAC; - key_blob.hdr.bType = PLAINTEXTKEYBLOB; - key_blob.hdr.bVersion = CUR_BLOB_VERSION; - key_blob.hdr.reserved = 0; - /* - * Note: RC2 is not really used, but that can be used to - * import HMAC keys of up to 16 byte long. - * CRYPT_IPSEC_HMAC_KEY flag for CryptImportKey() is needed to - * be able to import longer keys (HMAC-SHA1 uses 20-byte key). - */ - key_blob.hdr.aiKeyAlg = CALG_RC2; - key_blob.len = key_len; - if (key_len > sizeof(key_blob.key)) - return NULL; - os_memcpy(key_blob.key, key, key_len); - break; - default: - return NULL; - } - - ctx = os_zalloc(sizeof(*ctx)); - if (ctx == NULL) - return NULL; - - ctx->alg = alg; - - if (!CryptAcquireContext(&ctx->prov, NULL, NULL, PROV_RSA_FULL, 0)) { - cryptoapi_report_error("CryptAcquireContext"); - os_free(ctx); - return NULL; - } - - if (calg == CALG_HMAC) { -#ifndef CRYPT_IPSEC_HMAC_KEY -#define CRYPT_IPSEC_HMAC_KEY 0x00000100 -#endif - if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob, - sizeof(key_blob), 0, CRYPT_IPSEC_HMAC_KEY, - &ctx->key)) { - cryptoapi_report_error("CryptImportKey"); - CryptReleaseContext(ctx->prov, 0); - os_free(ctx); - return NULL; - } - } - - if (!CryptCreateHash(ctx->prov, calg, ctx->key, 0, &ctx->hash)) { - cryptoapi_report_error("CryptCreateHash"); - CryptReleaseContext(ctx->prov, 0); - os_free(ctx); - return NULL; - } - - if (calg == CALG_HMAC) { - HMAC_INFO info; - os_memset(&info, 0, sizeof(info)); - switch (alg) { - case CRYPTO_HASH_ALG_HMAC_MD5: - info.HashAlgid = CALG_MD5; - break; - case CRYPTO_HASH_ALG_HMAC_SHA1: - info.HashAlgid = CALG_SHA; - break; - default: - /* unreachable */ - break; - } - - if (!CryptSetHashParam(ctx->hash, HP_HMAC_INFO, (BYTE *) &info, - 0)) { - cryptoapi_report_error("CryptSetHashParam"); - CryptDestroyHash(ctx->hash); - CryptReleaseContext(ctx->prov, 0); - os_free(ctx); - return NULL; - } - } - - return ctx; -} - - -void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) -{ - if (ctx == NULL || ctx->error) - return; - - if (!CryptHashData(ctx->hash, (BYTE *) data, len, 0)) { - cryptoapi_report_error("CryptHashData"); - ctx->error = 1; - } -} - - -int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) -{ - int ret = 0; - DWORD hlen; - - if (ctx == NULL) - return -2; - - if (mac == NULL || len == NULL) - goto done; - - if (ctx->error) { - ret = -2; - goto done; - } - - hlen = *len; - if (!CryptGetHashParam(ctx->hash, HP_HASHVAL, mac, &hlen, 0)) { - cryptoapi_report_error("CryptGetHashParam"); - ret = -2; - } - *len = hlen; - -done: - if (ctx->alg == CRYPTO_HASH_ALG_HMAC_SHA1 || - ctx->alg == CRYPTO_HASH_ALG_HMAC_MD5) - CryptDestroyKey(ctx->key); - - os_free(ctx); - - return ret; -} - - -struct crypto_cipher { - HCRYPTPROV prov; - HCRYPTKEY key; -}; - - -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; - struct { - BLOBHEADER hdr; - DWORD len; - BYTE key[32]; - } key_blob; - DWORD mode = CRYPT_MODE_CBC; - - key_blob.hdr.bType = PLAINTEXTKEYBLOB; - key_blob.hdr.bVersion = CUR_BLOB_VERSION; - key_blob.hdr.reserved = 0; - key_blob.len = key_len; - if (key_len > sizeof(key_blob.key)) - return NULL; - os_memcpy(key_blob.key, key, key_len); - - switch (alg) { - case CRYPTO_CIPHER_ALG_AES: - if (key_len == 32) - key_blob.hdr.aiKeyAlg = CALG_AES_256; - else if (key_len == 24) - key_blob.hdr.aiKeyAlg = CALG_AES_192; - else - key_blob.hdr.aiKeyAlg = CALG_AES_128; - break; - case CRYPTO_CIPHER_ALG_3DES: - key_blob.hdr.aiKeyAlg = CALG_3DES; - break; - case CRYPTO_CIPHER_ALG_DES: - key_blob.hdr.aiKeyAlg = CALG_DES; - break; - case CRYPTO_CIPHER_ALG_RC2: - key_blob.hdr.aiKeyAlg = CALG_RC2; - break; - case CRYPTO_CIPHER_ALG_RC4: - key_blob.hdr.aiKeyAlg = CALG_RC4; - break; - default: - return NULL; - } - - ctx = os_zalloc(sizeof(*ctx)); - if (ctx == NULL) - return NULL; - - if (!CryptAcquireContext(&ctx->prov, NULL, MS_ENH_RSA_AES_PROV, - PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { - cryptoapi_report_error("CryptAcquireContext"); - goto fail1; - } - - if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob, - sizeof(key_blob), 0, 0, &ctx->key)) { - cryptoapi_report_error("CryptImportKey"); - goto fail2; - } - - if (!CryptSetKeyParam(ctx->key, KP_MODE, (BYTE *) &mode, 0)) { - cryptoapi_report_error("CryptSetKeyParam(KP_MODE)"); - goto fail3; - } - - if (iv && !CryptSetKeyParam(ctx->key, KP_IV, (BYTE *) iv, 0)) { - cryptoapi_report_error("CryptSetKeyParam(KP_IV)"); - goto fail3; - } - - return ctx; - -fail3: - CryptDestroyKey(ctx->key); -fail2: - CryptReleaseContext(ctx->prov, 0); -fail1: - os_free(ctx); - return NULL; -} - - -int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, - u8 *crypt, size_t len) -{ - DWORD dlen; - - os_memcpy(crypt, plain, len); - dlen = len; - if (!CryptEncrypt(ctx->key, 0, FALSE, 0, crypt, &dlen, len)) { - cryptoapi_report_error("CryptEncrypt"); - os_memset(crypt, 0, len); - return -1; - } - - return 0; -} - - -int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, - u8 *plain, size_t len) -{ - DWORD dlen; - - os_memcpy(plain, crypt, len); - dlen = len; - if (!CryptDecrypt(ctx->key, 0, FALSE, 0, plain, &dlen)) { - cryptoapi_report_error("CryptDecrypt"); - return -1; - } - - return 0; -} - - -void crypto_cipher_deinit(struct crypto_cipher *ctx) -{ - CryptDestroyKey(ctx->key); - CryptReleaseContext(ctx->prov, 0); - os_free(ctx); -} - - -struct crypto_public_key { - HCRYPTPROV prov; - HCRYPTKEY rsa; -}; - -struct crypto_private_key { - HCRYPTPROV prov; - HCRYPTKEY rsa; -}; - - -struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) -{ - /* Use crypto_public_key_from_cert() instead. */ - return NULL; -} - - -struct crypto_private_key * crypto_private_key_import(const u8 *key, - size_t len, - const char *passwd) -{ - /* TODO */ - return NULL; -} - - -struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, - size_t len) -{ - struct crypto_public_key *pk; - PCCERT_CONTEXT cc; - - pk = os_zalloc(sizeof(*pk)); - if (pk == NULL) - return NULL; - - cc = CertCreateCertificateContext(X509_ASN_ENCODING | - PKCS_7_ASN_ENCODING, buf, len); - if (!cc) { - cryptoapi_report_error("CryptCreateCertificateContext"); - os_free(pk); - return NULL; - } - - if (!CryptAcquireContext(&pk->prov, NULL, MS_DEF_PROV, PROV_RSA_FULL, - 0)) { - cryptoapi_report_error("CryptAcquireContext"); - os_free(pk); - CertFreeCertificateContext(cc); - return NULL; - } - - if (!CryptImportPublicKeyInfo(pk->prov, X509_ASN_ENCODING | - PKCS_7_ASN_ENCODING, - &cc->pCertInfo->SubjectPublicKeyInfo, - &pk->rsa)) { - cryptoapi_report_error("CryptImportPublicKeyInfo"); - CryptReleaseContext(pk->prov, 0); - os_free(pk); - CertFreeCertificateContext(cc); - return NULL; - } - - CertFreeCertificateContext(cc); - - return pk; -} - - -int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, - const u8 *in, size_t inlen, - u8 *out, size_t *outlen) -{ - DWORD clen; - u8 *tmp; - size_t i; - - if (*outlen < inlen) - return -1; - tmp = malloc(*outlen); - if (tmp == NULL) - return -1; - - os_memcpy(tmp, in, inlen); - clen = inlen; - if (!CryptEncrypt(key->rsa, 0, TRUE, 0, tmp, &clen, *outlen)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: Failed to encrypt using " - "public key: %d", (int) GetLastError()); - os_free(tmp); - return -1; - } - - *outlen = clen; - - /* Reverse the output */ - for (i = 0; i < *outlen; i++) - out[i] = tmp[*outlen - 1 - i]; - - os_free(tmp); - - return 0; -} - - -int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, - const u8 *in, size_t inlen, - u8 *out, size_t *outlen) -{ - /* TODO */ - return -1; -} - - -void crypto_public_key_free(struct crypto_public_key *key) -{ - if (key) { - CryptDestroyKey(key->rsa); - CryptReleaseContext(key->prov, 0); - os_free(key); - } -} - - -void crypto_private_key_free(struct crypto_private_key *key) -{ - if (key) { - CryptDestroyKey(key->rsa); - CryptReleaseContext(key->prov, 0); - os_free(key); - } -} - - -int crypto_global_init(void) -{ - return mingw_load_crypto_func(); -} - - -void crypto_global_deinit(void) -{ -} - - -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) -{ - /* TODO */ - return -1; -} diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c index 7137c27d0e8c5..581005df3e39e 100644 --- a/src/crypto/crypto_module_tests.c +++ b/src/crypto/crypto_module_tests.c @@ -161,7 +161,7 @@ struct omac1_test_vector { u8 tag[16]; }; -static struct omac1_test_vector omac1_test_vectors[] = +static const struct omac1_test_vector omac1_test_vectors[] = { { { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, @@ -210,7 +210,8 @@ static struct omac1_test_vector omac1_test_vectors[] = }; -static int test_omac1_vector(struct omac1_test_vector *tv, unsigned int i) +static int test_omac1_vector(const struct omac1_test_vector *tv, + unsigned int i) { u8 key[] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, @@ -515,6 +516,7 @@ static int test_key_wrap(void) 0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82, 0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5 }; +#ifndef CONFIG_BORINGSSL /* RFC 3394 - Test vector 4.2 */ u8 kek42[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, @@ -530,6 +532,7 @@ static int test_key_wrap(void) 0xF9, 0x2B, 0x5B, 0x97, 0xC0, 0x50, 0xAE, 0xD2, 0x46, 0x8A, 0xB8, 0xA1, 0x7A, 0xD8, 0x4E, 0x5D }; +#endif /* CONFIG_BORINGSSL */ /* RFC 3394 - Test vector 4.3 */ u8 kek43[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, @@ -546,6 +549,7 @@ static int test_key_wrap(void) 0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A, 0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7, }; +#ifndef CONFIG_BORINGSSL /* RFC 3394 - Test vector 4.4 */ u8 kek44[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, @@ -563,6 +567,7 @@ static int test_key_wrap(void) 0xE1, 0xC6, 0xC7, 0xDD, 0xEE, 0x72, 0x5A, 0x93, 0x6B, 0xA8, 0x14, 0x91, 0x5C, 0x67, 0x62, 0xD2 }; +#endif /* CONFIG_BORINGSSL */ /* RFC 3394 - Test vector 4.5 */ u8 kek45[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, @@ -623,6 +628,7 @@ static int test_key_wrap(void) ret++; } +#ifndef CONFIG_BORINGSSL wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.2"); if (aes_wrap(kek42, sizeof(kek42), sizeof(plain42) / 8, plain42, result)) { @@ -642,6 +648,7 @@ static int test_key_wrap(void) wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed"); ret++; } +#endif /* CONFIG_BORINGSSL */ wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.3"); if (aes_wrap(kek43, sizeof(kek43), sizeof(plain43) / 8, plain43, @@ -663,6 +670,7 @@ static int test_key_wrap(void) ret++; } +#ifndef CONFIG_BORINGSSL wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.4"); if (aes_wrap(kek44, sizeof(kek44), sizeof(plain44) / 8, plain44, result)) { @@ -682,6 +690,7 @@ static int test_key_wrap(void) wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed"); ret++; } +#endif /* CONFIG_BORINGSSL */ wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.5"); if (aes_wrap(kek45, sizeof(kek45), sizeof(plain45) / 8, plain45, @@ -732,6 +741,7 @@ static int test_key_wrap(void) static int test_md5(void) { +#ifndef CONFIG_FIPS struct { char *data; char *hash; @@ -810,6 +820,10 @@ static int test_md5(void) wpa_printf(MSG_INFO, "MD5 test cases passed"); return errors; +#else /* CONFIG_FIPS */ + wpa_printf(MSG_INFO, "MD5 test cases skipped due to CONFIG_FIPS"); + return 0; +#endif /* CONFIG_FIPS */ } @@ -841,6 +855,7 @@ static int test_eap_fast(void) 0x38, 0x4B, 0x7A, 0x85, 0xBE, 0x16, 0x4D, 0x27, 0x33, 0xD5, 0x24, 0x79, 0x87, 0xB1, 0xC5, 0xA2 }; +#ifndef CONFIG_FIPS const u8 key_block[] = { 0x59, 0x59, 0xBE, 0x8E, 0x41, 0x3A, 0x77, 0x74, 0x8B, 0xB2, 0xE5, 0xD3, 0x60, 0xAC, 0x4D, 0x35, @@ -857,6 +872,7 @@ static int test_eap_fast(void) 0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98, 0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71 }; +#endif /* CONFIG_FIPS */ const u8 sks[] = { 0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05, 0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96, @@ -931,6 +947,7 @@ static int test_eap_fast(void) errors++; } +#ifndef CONFIG_FIPS wpa_printf(MSG_INFO, "- PRF (TLS, SHA1/MD5) test case / key_block"); if (tls_prf_sha1_md5(master_secret, sizeof(master_secret), "key expansion", seed, sizeof(seed), @@ -939,6 +956,7 @@ static int test_eap_fast(void) wpa_printf(MSG_INFO, "PRF test - FAILED!"); errors++; } +#endif /* CONFIG_FIPS */ wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / IMCK"); if (sha1_t_prf(sks, sizeof(sks), "Inner Methods Compound Keys", @@ -983,14 +1001,14 @@ static int test_eap_fast(void) } -static u8 key0[] = +static const u8 key0[] = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b }; -static u8 data0[] = "Hi There"; -static u8 prf0[] = +static const u8 data0[] = "Hi There"; +static const u8 prf0[] = { 0xbc, 0xd4, 0xc6, 0x50, 0xb3, 0x0b, 0x96, 0x84, 0x95, 0x18, 0x29, 0xe0, 0xd7, 0x5f, 0x9d, 0x54, @@ -1002,9 +1020,9 @@ static u8 prf0[] = 0xdb, 0x83, 0x73, 0x69, 0x83, 0x56, 0xcf, 0x5a }; -static u8 key1[] = "Jefe"; -static u8 data1[] = "what do ya want for nothing?"; -static u8 prf1[] = +static const u8 key1[] = "Jefe"; +static const u8 data1[] = "what do ya want for nothing?"; +static const u8 prf1[] = { 0x51, 0xf4, 0xde, 0x5b, 0x33, 0xf2, 0x49, 0xad, 0xf8, 0x1a, 0xeb, 0x71, 0x3a, 0x3c, 0x20, 0xf4, @@ -1017,13 +1035,13 @@ static u8 prf1[] = }; -static u8 key2[] = +static const u8 key2[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }; -static u8 data2[] = +static const u8 data2[] = { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, @@ -1033,7 +1051,7 @@ static u8 data2[] = 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd }; -static u8 prf2[] = +static const u8 prf2[] = { 0xe1, 0xac, 0x54, 0x6e, 0xc4, 0xcb, 0x63, 0x6f, 0x99, 0x76, 0x48, 0x7b, 0xe5, 0xc8, 0x6b, 0xe1, @@ -1052,7 +1070,7 @@ struct passphrase_test { char psk[32]; }; -static struct passphrase_test passphrase_tests[] = +static const struct passphrase_test passphrase_tests[] = { { "password", @@ -1097,7 +1115,7 @@ struct rfc6070_test { size_t dk_len; }; -static struct rfc6070_test rfc6070_tests[] = +static const struct rfc6070_test rfc6070_tests[] = { { "password", @@ -1214,7 +1232,7 @@ static int test_sha1(void) wpa_printf(MSG_INFO, "PBKDF2-SHA1 Passphrase test cases:"); for (i = 0; i < NUM_PASSPHRASE_TESTS; i++) { u8 psk[32]; - struct passphrase_test *test = &passphrase_tests[i]; + const struct passphrase_test *test = &passphrase_tests[i]; if (pbkdf2_sha1(test->passphrase, (const u8 *) test->ssid, strlen(test->ssid), @@ -1230,7 +1248,7 @@ static int test_sha1(void) wpa_printf(MSG_INFO, "PBKDF2-SHA1 test cases (RFC 6070):"); for (i = 0; i < NUM_RFC6070_TESTS; i++) { u8 dk[25]; - struct rfc6070_test *test = &rfc6070_tests[i]; + const struct rfc6070_test *test = &rfc6070_tests[i]; if (pbkdf2_sha1(test->p, (const u8 *) test->s, strlen(test->s), test->c, dk, test->dk_len) == 0 && @@ -1248,7 +1266,7 @@ static int test_sha1(void) } -struct { +const struct { char *data; u8 hash[32]; } tests[] = { @@ -1272,7 +1290,7 @@ struct { } }; -struct hmac_test { +const struct hmac_test { u8 key[80]; size_t key_len; u8 data[128]; @@ -1513,7 +1531,7 @@ static int test_sha256(void) } for (i = 0; i < ARRAY_SIZE(hmac_tests); i++) { - struct hmac_test *t = &hmac_tests[i]; + const struct hmac_test *t = &hmac_tests[i]; wpa_printf(MSG_INFO, "HMAC-SHA256 test case %d:", i + 1); @@ -1563,6 +1581,7 @@ static int test_sha256(void) static int test_ms_funcs(void) { +#ifndef CONFIG_FIPS /* Test vector from RFC2759 example */ char *username = "User"; char *password = "clientPass"; @@ -1655,6 +1674,10 @@ static int test_ms_funcs(void) wpa_printf(MSG_INFO, "ms_funcs test cases passed"); return errors; +#else /* CONFIG_FIPS */ + wpa_printf(MSG_INFO, "ms_funcs test cases skipped due to CONFIG_FIPS"); + return 0; +#endif /* CONFIG_FIPS */ } diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index f158ef43a645c..6cff75c64ae5a 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -93,10 +93,12 @@ static int openssl_digest_vector(const EVP_MD *type, size_t num_elem, } +#ifndef CONFIG_FIPS int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac); } +#endif /* CONFIG_FIPS */ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) @@ -120,6 +122,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) } +#ifndef CONFIG_NO_RC4 int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len) { @@ -155,12 +158,15 @@ out: return res; #endif /* OPENSSL_NO_RC4 */ } +#endif /* CONFIG_NO_RC4 */ +#ifndef CONFIG_FIPS int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac); } +#endif /* CONFIG_FIPS */ int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) @@ -297,6 +303,9 @@ void aes_decrypt_deinit(void *ctx) } +#ifndef CONFIG_FIPS +#ifndef CONFIG_OPENSSL_INTERNAL_AES_WRAP + int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher) { AES_KEY actx; @@ -323,6 +332,59 @@ int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, return res <= 0 ? -1 : 0; } +#endif /* CONFIG_OPENSSL_INTERNAL_AES_WRAP */ +#endif /* CONFIG_FIPS */ + + +int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + EVP_CIPHER_CTX ctx; + int clen, len; + u8 buf[16]; + + EVP_CIPHER_CTX_init(&ctx); + if (EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1) + return -1; + EVP_CIPHER_CTX_set_padding(&ctx, 0); + + clen = data_len; + if (EVP_EncryptUpdate(&ctx, data, &clen, data, data_len) != 1 || + clen != (int) data_len) + return -1; + + len = sizeof(buf); + if (EVP_EncryptFinal_ex(&ctx, buf, &len) != 1 || len != 0) + return -1; + EVP_CIPHER_CTX_cleanup(&ctx); + + return 0; +} + + +int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + EVP_CIPHER_CTX ctx; + int plen, len; + u8 buf[16]; + + EVP_CIPHER_CTX_init(&ctx); + if (EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1) + return -1; + EVP_CIPHER_CTX_set_padding(&ctx, 0); + + plen = data_len; + if (EVP_DecryptUpdate(&ctx, data, &plen, data, data_len) != 1 || + plen != (int) data_len) + return -1; + + len = sizeof(buf); + if (EVP_DecryptFinal_ex(&ctx, buf, &len) != 1 || len != 0) + return -1; + EVP_CIPHER_CTX_cleanup(&ctx); + + return 0; +} + int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, @@ -380,11 +442,13 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, return NULL; switch (alg) { +#ifndef CONFIG_NO_RC4 #ifndef OPENSSL_NO_RC4 case CRYPTO_CIPHER_ALG_RC4: cipher = EVP_rc4(); break; #endif /* OPENSSL_NO_RC4 */ +#endif /* CONFIG_NO_RC4 */ #ifndef OPENSSL_NO_AES case CRYPTO_CIPHER_ALG_AES: switch (key_len) { @@ -1057,6 +1121,42 @@ int crypto_bignum_is_one(const struct crypto_bignum *a) } +int crypto_bignum_legendre(const struct crypto_bignum *a, + const struct crypto_bignum *p) +{ + BN_CTX *bnctx; + BIGNUM *exp = NULL, *tmp = NULL; + int res = -2; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -2; + + exp = BN_new(); + tmp = BN_new(); + if (!exp || !tmp || + /* exp = (p-1) / 2 */ + !BN_sub(exp, (const BIGNUM *) p, BN_value_one()) || + !BN_rshift1(exp, exp) || + !BN_mod_exp(tmp, (const BIGNUM *) a, exp, (const BIGNUM *) p, + bnctx)) + goto fail; + + if (BN_is_word(tmp, 1)) + res = 1; + else if (BN_is_zero(tmp)) + res = 0; + else + res = -1; + +fail: + BN_clear_free(tmp); + BN_clear_free(exp); + BN_CTX_free(bnctx); + return res; +} + + #ifdef CONFIG_ECC struct crypto_ec { @@ -1064,6 +1164,8 @@ struct crypto_ec { BN_CTX *bnctx; BIGNUM *prime; BIGNUM *order; + BIGNUM *a; + BIGNUM *b; }; struct crypto_ec * crypto_ec_init(int group) @@ -1088,6 +1190,26 @@ struct crypto_ec * crypto_ec_init(int group) 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: return NULL; } @@ -1100,9 +1222,11 @@ struct crypto_ec * crypto_ec_init(int group) e->group = EC_GROUP_new_by_curve_name(nid); e->prime = BN_new(); e->order = BN_new(); + e->a = BN_new(); + e->b = BN_new(); if (e->group == NULL || e->bnctx == NULL || e->prime == NULL || - e->order == NULL || - !EC_GROUP_get_curve_GFp(e->group, e->prime, NULL, NULL, e->bnctx) || + e->order == NULL || e->a == NULL || e->b == NULL || + !EC_GROUP_get_curve_GFp(e->group, e->prime, e->a, e->b, e->bnctx) || !EC_GROUP_get_order(e->group, e->order, e->bnctx)) { crypto_ec_deinit(e); e = NULL; @@ -1116,6 +1240,8 @@ void crypto_ec_deinit(struct crypto_ec *e) { if (e == NULL) return; + BN_clear_free(e->b); + BN_clear_free(e->a); BN_clear_free(e->order); BN_clear_free(e->prime); EC_GROUP_free(e->group); @@ -1263,6 +1389,33 @@ int crypto_ec_point_solve_y_coord(struct crypto_ec *e, } +struct crypto_bignum * +crypto_ec_point_compute_y_sqr(struct crypto_ec *e, + const struct crypto_bignum *x) +{ + BIGNUM *tmp, *tmp2, *y_sqr = NULL; + + tmp = BN_new(); + tmp2 = BN_new(); + + /* y^2 = x^3 + ax + b */ + if (tmp && tmp2 && + BN_mod_sqr(tmp, (const BIGNUM *) x, e->prime, e->bnctx) && + BN_mod_mul(tmp, tmp, (const BIGNUM *) x, e->prime, e->bnctx) && + BN_mod_mul(tmp2, e->a, (const BIGNUM *) x, e->prime, e->bnctx) && + BN_mod_add_quick(tmp2, tmp2, tmp, e->prime) && + BN_mod_add_quick(tmp2, tmp2, e->b, e->prime)) { + y_sqr = tmp2; + tmp2 = NULL; + } + + BN_clear_free(tmp); + BN_clear_free(tmp2); + + return (struct crypto_bignum *) y_sqr; +} + + int crypto_ec_point_is_at_infinity(struct crypto_ec *e, const struct crypto_ec_point *p) { @@ -1273,7 +1426,17 @@ int crypto_ec_point_is_at_infinity(struct crypto_ec *e, int crypto_ec_point_is_on_curve(struct crypto_ec *e, const struct crypto_ec_point *p) { - return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p, e->bnctx); + return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p, + e->bnctx) == 1; +} + + +int crypto_ec_point_cmp(const struct crypto_ec *e, + const struct crypto_ec_point *a, + const struct crypto_ec_point *b) +{ + return EC_POINT_cmp(e->group, (const EC_POINT *) a, + (const EC_POINT *) b, e->bnctx); } #endif /* CONFIG_ECC */ diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c index d3b263196e2d1..3aeb2bbc60af3 100644 --- a/src/crypto/dh_groups.c +++ b/src/crypto/dh_groups.c @@ -1153,7 +1153,7 @@ dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \ dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe } -static struct dh_group dh_groups[] = { +static const struct dh_group dh_groups[] = { DH_GROUP(5, 1), #ifdef ALL_DH_GROUPS DH_GROUP(1, 1), diff --git a/src/crypto/fips_prf_openssl.c b/src/crypto/fips_prf_openssl.c index d69eceabff1c5..fb03efcd4ffc2 100644 --- a/src/crypto/fips_prf_openssl.c +++ b/src/crypto/fips_prf_openssl.c @@ -13,13 +13,21 @@ #include "crypto.h" -static void sha1_transform(u8 *state, const u8 data[64]) +static void sha1_transform(u32 *state, const u8 data[64]) { SHA_CTX context; os_memset(&context, 0, sizeof(context)); - os_memcpy(&context.h0, state, 5 * 4); + context.h0 = state[0]; + context.h1 = state[1]; + context.h2 = state[2]; + context.h3 = state[3]; + context.h4 = state[4]; SHA1_Transform(&context, data); - os_memcpy(state, &context.h0, 5 * 4); + state[0] = context.h0; + state[1] = context.h1; + state[2] = context.h2; + state[3] = context.h3; + state[4] = context.h4; } @@ -53,7 +61,7 @@ int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) /* w_i = G(t, XVAL) */ os_memcpy(_t, t, 20); - sha1_transform((u8 *) _t, xkey); + sha1_transform(_t, xkey); _t[0] = host_to_be32(_t[0]); _t[1] = host_to_be32(_t[1]); _t[2] = host_to_be32(_t[2]); diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c index 49a5c1c245d67..053d203cb65bb 100644 --- a/src/crypto/ms_funcs.c +++ b/src/crypto/ms_funcs.c @@ -78,9 +78,8 @@ static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len, * @challenge: 8-octet Challenge (OUT) * Returns: 0 on success, -1 on failure */ -static int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, - const u8 *username, size_t username_len, - u8 *challenge) +int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, u8 *challenge) { u8 hash[SHA1_MAC_LEN]; const unsigned char *addr[3]; @@ -175,9 +174,8 @@ int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, u8 password_hash[16]; if (challenge_hash(peer_challenge, auth_challenge, username, - username_len, challenge)) - return -1; - if (nt_password_hash(password, password_len, password_hash)) + username_len, challenge) || + nt_password_hash(password, password_len, password_hash)) return -1; challenge_response(challenge, password_hash, response); return 0; @@ -257,12 +255,9 @@ int generate_authenticator_response_pwhash( addr2[1] = challenge; addr2[2] = magic2; - if (hash_nt_password_hash(password_hash, password_hash_hash)) - return -1; - if (sha1_vector(3, addr1, len1, response)) - return -1; - - if (challenge_hash(peer_challenge, auth_challenge, username, + if (hash_nt_password_hash(password_hash, password_hash_hash) || + sha1_vector(3, addr1, len1, response) || + challenge_hash(peer_challenge, auth_challenge, username, username_len, challenge)) return -1; return sha1_vector(3, addr2, len2, response); @@ -417,6 +412,8 @@ int get_asymetric_start_key(const u8 *master_key, u8 *session_key, } +#ifndef CONFIG_NO_RC4 + #define PWBLOCK_LEN 516 /** @@ -436,10 +433,8 @@ int encrypt_pw_block_with_password_hash( os_memset(pw_block, 0, PWBLOCK_LEN); - if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0) - return -1; - - if (ucs2_len > 256) + if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0 + || ucs2_len > 256) return -1; offset = (256 - ucs2_len) * 2; @@ -484,6 +479,8 @@ int new_password_encrypted_with_old_nt_password_hash( return 0; } +#endif /* CONFIG_NO_RC4 */ + /** * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13 diff --git a/src/crypto/ms_funcs.h b/src/crypto/ms_funcs.h index bd9bfee95b7c2..b5b5918e1a555 100644 --- a/src/crypto/ms_funcs.h +++ b/src/crypto/ms_funcs.h @@ -33,6 +33,8 @@ int nt_challenge_response(const u8 *challenge, const u8 *password, void 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, u8 *password_hash); int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash); diff --git a/src/crypto/random.c b/src/crypto/random.c index bc758aa572327..3a86a93a46a8b 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -181,6 +181,7 @@ int random_get_bytes(void *buf, size_t len) #ifdef CONFIG_FIPS /* Mix in additional entropy from the crypto module */ + bytes = buf; left = len; while (left) { size_t siz, i; diff --git a/src/crypto/sha1-tlsprf.c b/src/crypto/sha1-tlsprf.c index 0effd9b76dd89..f9bc0ebf6e3d0 100644 --- a/src/crypto/sha1-tlsprf.c +++ b/src/crypto/sha1-tlsprf.c @@ -95,5 +95,10 @@ int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label, SHA1_pos++; } + os_memset(A_MD5, 0, MD5_MAC_LEN); + os_memset(P_MD5, 0, MD5_MAC_LEN); + os_memset(A_SHA1, 0, SHA1_MAC_LEN); + os_memset(P_SHA1, 0, SHA1_MAC_LEN); + return 0; } diff --git a/src/crypto/sha1-tprf.c b/src/crypto/sha1-tprf.c index a52949462f77b..562510f8937d0 100644 --- a/src/crypto/sha1-tprf.c +++ b/src/crypto/sha1-tprf.c @@ -66,5 +66,7 @@ int sha1_t_prf(const u8 *key, size_t key_len, const char *label, len[0] = SHA1_MAC_LEN; } + os_memset(hash, 0, SHA1_MAC_LEN); + return 0; } diff --git a/src/crypto/sha256-kdf.c b/src/crypto/sha256-kdf.c index d8a1beb32e908..e7509ce41aba4 100644 --- a/src/crypto/sha256-kdf.c +++ b/src/crypto/sha256-kdf.c @@ -61,6 +61,7 @@ int hmac_sha256_kdf(const u8 *secret, size_t secret_len, if (iter == 255) { os_memset(out, 0, outlen); + os_memset(T, 0, SHA256_MAC_LEN); return -1; } iter++; @@ -68,9 +69,11 @@ int hmac_sha256_kdf(const u8 *secret, size_t secret_len, if (hmac_sha256_vector(secret, secret_len, 4, addr, len, T) < 0) { os_memset(out, 0, outlen); + os_memset(T, 0, SHA256_MAC_LEN); return -1; } } + os_memset(T, 0, SHA256_MAC_LEN); return 0; } diff --git a/src/crypto/sha384-prf.c b/src/crypto/sha384-prf.c new file mode 100644 index 0000000000000..653920ba283d2 --- /dev/null +++ b/src/crypto/sha384-prf.c @@ -0,0 +1,100 @@ +/* + * SHA384-based KDF (IEEE 802.11ac) + * Copyright (c) 2003-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" + +#include "common.h" +#include "sha384.h" +#include "crypto.h" + + +/** + * sha384_prf - SHA384-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 + * + * 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) +{ + sha384_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8); +} + + +/** + * sha384_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 + * + * 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) +{ + u16 counter = 1; + size_t pos, plen; + u8 hash[SHA384_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 >= SHA384_MAC_LEN) { + hmac_sha384_vector(key, key_len, 4, addr, len, + &buf[pos]); + pos += SHA384_MAC_LEN; + } else { + hmac_sha384_vector(key, key_len, 4, addr, len, hash); + 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)); +} diff --git a/src/crypto/sha384.h b/src/crypto/sha384.h index e6a1fe41e1a10..3deafa59ec290 100644 --- a/src/crypto/sha384.h +++ b/src/crypto/sha384.h @@ -15,5 +15,10 @@ 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); #endif /* SHA384_H */ diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 9ae95a66c9ed6..2e562339cc5c0 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -11,9 +11,7 @@ struct tls_connection; -struct tls_keys { - const u8 *master_key; /* TLS master secret */ - size_t master_key_len; +struct tls_random { const u8 *client_random; size_t client_random_len; const u8 *server_random; @@ -81,6 +79,7 @@ struct tls_config { int fips_mode; int cert_in_cb; const char *openssl_ciphers; + unsigned int tls_session_lifetime; void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); @@ -95,6 +94,7 @@ struct tls_config { #define TLS_CONN_DISABLE_TLSv1_1 BIT(5) #define TLS_CONN_DISABLE_TLSv1_2 BIT(6) #define TLS_CONN_EAP_FAST BIT(7) +#define TLS_CONN_DISABLE_TLSv1_0 BIT(8) /** * struct tls_connection_params - Parameters for TLS connection @@ -255,6 +255,7 @@ int tls_connection_established(void *tls_ctx, struct tls_connection *conn); int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn); enum { + TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN = -4, TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3, TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2 }; @@ -265,10 +266,12 @@ enum { * @conn: Connection context data from tls_connection_init() * @params: Connection parameters * Returns: 0 on success, -1 on failure, - * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing - * PKCS#11 engine failure, or + * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine + * failure, or * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the - * PKCS#11 engine private key. + * PKCS#11 engine private key, or + * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine + * failure. */ int __must_check tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, @@ -279,10 +282,12 @@ tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, * @tls_ctx: TLS context data from tls_init() * @params: Global TLS parameters * Returns: 0 on success, -1 on failure, - * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing - * PKCS#11 engine failure, or + * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine + * failure, or * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the - * PKCS#11 engine private key. + * PKCS#11 engine private key, or + * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine + * failure. */ int __must_check tls_global_set_params( void *tls_ctx, const struct tls_connection_params *params); @@ -301,22 +306,28 @@ int __must_check tls_global_set_verify(void *tls_ctx, int check_crl); * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @verify_peer: 1 = verify peer certificate + * @flags: Connection flags (TLS_CONN_*) + * @session_ctx: Session caching context or %NULL to use default + * @session_ctx_len: Length of @session_ctx in bytes. * Returns: 0 on success, -1 on failure */ int __must_check tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, - int verify_peer); + int verify_peer, + unsigned int flags, + const u8 *session_ctx, + size_t session_ctx_len); /** - * tls_connection_get_keys - Get master key and random data from TLS connection + * tls_connection_get_random - Get random data from TLS connection * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() - * @keys: Structure of key/random data (filled on success) + * @data: Structure of client/server random data (filled on success) * Returns: 0 on success, -1 on failure */ -int __must_check tls_connection_get_keys(void *tls_ctx, +int __must_check tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, - struct tls_keys *keys); + struct tls_random *data); /** * tls_connection_prf - Use TLS-PRF to derive keying material @@ -325,23 +336,22 @@ int __must_check tls_connection_get_keys(void *tls_ctx, * @label: Label (e.g., description of the key) for PRF * @server_random_first: seed is 0 = client_random|server_random, * 1 = server_random|client_random + * @skip_keyblock: Skip TLS key block from the beginning of PRF output * @out: Buffer for output data from TLS-PRF * @out_len: Length of the output buffer * Returns: 0 on success, -1 on failure * - * This function is optional to implement if tls_connection_get_keys() provides - * access to master secret and server/client random values. If these values are - * not exported from the TLS library, tls_connection_prf() is required so that - * further keying material can be derived from the master secret. If not - * implemented, the function will still need to be defined, but it can just - * return -1. Example implementation of this function is in tls_prf_sha1_md5() - * when it is called with seed set to client_random|server_random (or - * server_random|client_random). + * tls_connection_prf() is required so that further keying material can be + * derived from the master secret. Example implementation of this function is in + * tls_prf_sha1_md5() when it is called with seed set to + * client_random|server_random (or server_random|client_random). For TLSv1.2 and + * newer, a different PRF is needed, though. */ int __must_check tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, + int skip_keyblock, u8 *out, size_t out_len); /** @@ -461,6 +471,19 @@ int __must_check tls_connection_set_cipher_list(void *tls_ctx, u8 *ciphers); /** + * tls_get_version - Get the current TLS version number + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @buf: Buffer for returning the TLS version number + * @buflen: buf size + * Returns: 0 on success, -1 on failure + * + * Get the currently used TLS version number. + */ +int __must_check tls_get_version(void *tls_ctx, struct tls_connection *conn, + char *buf, size_t buflen); + +/** * tls_get_cipher - Get current cipher name * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() @@ -527,23 +550,6 @@ int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn); int tls_connection_get_write_alerts(void *tls_ctx, struct tls_connection *conn); -/** - * tls_connection_get_keyblock_size - Get TLS key_block size - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * Returns: Size of the key_block for the negotiated cipher suite or -1 on - * failure - */ -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn); - -/** - * tls_capabilities - Get supported TLS capabilities - * @tls_ctx: TLS context data from tls_init() - * Returns: Bit field of supported TLS capabilities (TLS_CAPABILITY_*) - */ -unsigned int tls_capabilities(void *tls_ctx); - typedef int (*tls_session_ticket_cb) (void *ctx, const u8 *ticket, size_t len, const u8 *client_random, const u8 *server_random, u8 *master_secret); @@ -569,4 +575,14 @@ void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags); int tls_get_library_version(char *buf, size_t buf_len); +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data); + +void tls_connection_set_success_data_resumed(struct tls_connection *conn); + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn); + +void tls_connection_remove_session(struct tls_connection *conn); + #endif /* TLS_H */ diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c index 65db6fcc25653..f994379b16b22 100644 --- a/src/crypto/tls_gnutls.c +++ b/src/crypto/tls_gnutls.c @@ -708,7 +708,8 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl) int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, - int verify_peer) + int verify_peer, unsigned int flags, + const u8 *session_ctx, size_t session_ctx_len) { if (conn == NULL || conn->session == NULL) return -1; @@ -722,8 +723,8 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, } -int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, - struct tls_keys *keys) +int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, + struct tls_random *keys) { #if GNUTLS_VERSION_NUMBER >= 0x030012 gnutls_datum_t client, server; @@ -747,9 +748,9 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, - u8 *out, size_t out_len) + int skip_keyblock, u8 *out, size_t out_len) { - if (conn == NULL || conn->session == NULL) + if (conn == NULL || conn->session == NULL || skip_keyblock) return -1; return gnutls_prf(conn->session, os_strlen(label), label, @@ -1426,6 +1427,14 @@ 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; +} + + int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { @@ -1476,30 +1485,39 @@ int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) } -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn) +int tls_connection_set_session_ticket_cb(void *tls_ctx, + struct tls_connection *conn, + tls_session_ticket_cb cb, void *ctx) { - /* TODO */ return -1; } -unsigned int tls_capabilities(void *tls_ctx) +int tls_get_library_version(char *buf, size_t buf_len) { - return 0; + return os_snprintf(buf, buf_len, "GnuTLS build=%s run=%s", + GNUTLS_VERSION, gnutls_check_version(NULL)); } -int tls_connection_set_session_ticket_cb(void *tls_ctx, - struct tls_connection *conn, - tls_session_ticket_cb cb, void *ctx) +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) { - return -1; } -int tls_get_library_version(char *buf, size_t buf_len) +void tls_connection_set_success_data_resumed(struct tls_connection *conn) +{ +} + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + return NULL; +} + + +void tls_connection_remove_session(struct tls_connection *conn) { - return os_snprintf(buf, buf_len, "GnuTLS build=%s run=%s", - GNUTLS_VERSION, gnutls_check_version(NULL)); } diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c index 0c955da29f1d5..704751d308fcf 100644 --- a/src/crypto/tls_internal.c +++ b/src/crypto/tls_internal.c @@ -192,26 +192,31 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (params->subject_match) { wpa_printf(MSG_INFO, "TLS: subject_match not supported"); + tlsv1_cred_free(cred); return -1; } if (params->altsubject_match) { wpa_printf(MSG_INFO, "TLS: altsubject_match not supported"); + tlsv1_cred_free(cred); return -1; } if (params->suffix_match) { wpa_printf(MSG_INFO, "TLS: suffix_match not supported"); + tlsv1_cred_free(cred); return -1; } if (params->domain_match) { wpa_printf(MSG_INFO, "TLS: domain_match not supported"); + tlsv1_cred_free(cred); return -1; } if (params->openssl_ciphers) { - wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported"); + wpa_printf(MSG_INFO, "TLS: openssl_ciphers not supported"); + tlsv1_cred_free(cred); return -1; } @@ -323,7 +328,8 @@ int tls_global_set_verify(void *tls_ctx, int check_crl) int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, - int verify_peer) + int verify_peer, unsigned int flags, + const u8 *session_ctx, size_t session_ctx_len) { #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) @@ -333,16 +339,30 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, } -int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, - struct tls_keys *keys) +int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, + struct tls_random *data) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_get_random(conn->client, data); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_random(conn->server, data); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +static int tls_get_keyblock_size(struct tls_connection *conn) { #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) - return tlsv1_client_get_keys(conn->client, keys); + return tlsv1_client_get_keyblock_size(conn->client); #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) - return tlsv1_server_get_keys(conn->server, keys); + return tlsv1_server_get_keyblock_size(conn->server); #endif /* CONFIG_TLS_INTERNAL_SERVER */ return -1; } @@ -350,23 +370,41 @@ int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, - u8 *out, size_t out_len) -{ + int skip_keyblock, u8 *out, size_t out_len) +{ + int ret = -1, skip = 0; + u8 *tmp_out = NULL; + u8 *_out = out; + + if (skip_keyblock) { + skip = tls_get_keyblock_size(conn); + if (skip < 0) + return -1; + tmp_out = os_malloc(skip + out_len); + if (!tmp_out) + return -1; + _out = tmp_out; + } + #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { - return tlsv1_client_prf(conn->client, label, - server_random_first, - out, out_len); + ret = tlsv1_client_prf(conn->client, label, + server_random_first, + _out, out_len); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) { - return tlsv1_server_prf(conn->server, label, - server_random_first, - out, out_len); + ret = tlsv1_server_prf(conn->server, label, + server_random_first, + _out, out_len); } #endif /* CONFIG_TLS_INTERNAL_SERVER */ - return -1; + if (ret == 0 && skip_keyblock) + os_memcpy(out, _out + skip, out_len); + bin_clear_free(tmp_out, skip); + + return ret; } @@ -580,6 +618,14 @@ 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; +} + + int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, char *buf, size_t buflen) { @@ -637,27 +683,6 @@ int tls_connection_get_write_alerts(void *tls_ctx, } -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn) -{ -#ifdef CONFIG_TLS_INTERNAL_CLIENT - if (conn->client) - return tlsv1_client_get_keyblock_size(conn->client); -#endif /* CONFIG_TLS_INTERNAL_CLIENT */ -#ifdef CONFIG_TLS_INTERNAL_SERVER - if (conn->server) - return tlsv1_server_get_keyblock_size(conn->server); -#endif /* CONFIG_TLS_INTERNAL_SERVER */ - return -1; -} - - -unsigned int tls_capabilities(void *tls_ctx) -{ - return 0; -} - - int tls_connection_set_session_ticket_cb(void *tls_ctx, struct tls_connection *conn, tls_session_ticket_cb cb, @@ -683,3 +708,26 @@ int tls_get_library_version(char *buf, size_t buf_len) { return os_snprintf(buf, buf_len, "internal"); } + + +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) +{ +} + + +void tls_connection_set_success_data_resumed(struct tls_connection *conn) +{ +} + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + return NULL; +} + + +void tls_connection_remove_session(struct tls_connection *conn) +{ +} diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c index a6d210afcf0f3..ae392ad8aa0f5 100644 --- a/src/crypto/tls_none.c +++ b/src/crypto/tls_none.c @@ -72,14 +72,15 @@ int tls_global_set_verify(void *tls_ctx, int check_crl) int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, - int verify_peer) + int verify_peer, unsigned int flags, + const u8 *session_ctx, size_t session_ctx_len) { return -1; } -int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, - struct tls_keys *keys) +int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, + struct tls_random *data) { return -1; } @@ -87,7 +88,7 @@ int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, - u8 *out, size_t out_len) + int skip_keyblock, u8 *out, size_t out_len) { return -1; } @@ -140,6 +141,13 @@ 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) +{ + return -1; +} + + int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, char *buf, size_t buflen) { @@ -181,20 +189,30 @@ int tls_connection_get_write_alerts(void *tls_ctx, } -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn) +int tls_get_library_version(char *buf, size_t buf_len) { - return -1; + return os_snprintf(buf, buf_len, "none"); } -unsigned int tls_capabilities(void *tls_ctx) +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) { - return 0; } -int tls_get_library_version(char *buf, size_t buf_len) +void tls_connection_set_success_data_resumed(struct tls_connection *conn) +{ +} + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + return NULL; +} + + +void tls_connection_remove_session(struct tls_connection *conn) { - return os_snprintf(buf, buf_len, "none"); } diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index 52db8fc076ac9..8b7b47bc256d5 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -1,6 +1,6 @@ /* * SSL/TLS interface functions for OpenSSL - * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -23,15 +23,19 @@ #ifndef OPENSSL_NO_ENGINE #include <openssl/engine.h> #endif /* OPENSSL_NO_ENGINE */ +#ifndef OPENSSL_NO_DSA +#include <openssl/dsa.h> +#endif +#ifndef OPENSSL_NO_DH +#include <openssl/dh.h> +#endif #include "common.h" #include "crypto.h" +#include "sha1.h" +#include "sha256.h" #include "tls.h" -#if defined(SSL_CTX_get_app_data) && defined(SSL_CTX_set_app_data) -#define OPENSSL_SUPPORTS_CTX_APP_DATA -#endif - #if OPENSSL_VERSION_NUMBER < 0x10000000L /* ERR_remove_thread_state replaces ERR_remove_state and the latter is * deprecated. However, OpenSSL 0.9.8 doesn't include @@ -70,6 +74,7 @@ static BIO * BIO_from_keystore(const char *key) #endif /* ANDROID */ static int tls_openssl_ref_count = 0; +static int tls_ex_idx_session = -1; struct tls_context { void (*event_cb)(void *ctx, enum tls_event ev, @@ -82,6 +87,11 @@ struct tls_context { static struct tls_context *tls_global = NULL; +struct tls_data { + SSL_CTX *ssl; + unsigned int tls_session_lifetime; +}; + struct tls_connection { struct tls_context *context; SSL_CTX *ssl_ctx; @@ -105,6 +115,7 @@ struct tls_connection { unsigned int cert_probe:1; unsigned int server_cert_only:1; unsigned int invalid_hb_used:1; + unsigned int success_data:1; u8 srv_cert_hash[32]; @@ -113,6 +124,11 @@ struct tls_connection { X509 *peer_cert; X509 *peer_issuer; X509 *peer_issuer_issuer; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + unsigned char client_random[SSL3_RANDOM_SIZE]; + unsigned char server_random[SSL3_RANDOM_SIZE]; +#endif }; @@ -735,8 +751,27 @@ static int tls_engine_load_dynamic_opensc(const char *opensc_so_path) #endif /* OPENSSL_NO_ENGINE */ +static void remove_session_cb(SSL_CTX *ctx, SSL_SESSION *sess) +{ + struct wpabuf *buf; + + if (tls_ex_idx_session < 0) + return; + buf = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + if (!buf) + return; + wpa_printf(MSG_DEBUG, + "OpenSSL: Free application session data %p (sess %p)", + buf, sess); + wpabuf_free(buf); + + SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL); +} + + void * tls_init(const struct tls_config *conf) { + struct tls_data *data; SSL_CTX *ssl; struct tls_context *context; const char *ciphers; @@ -748,7 +783,9 @@ void * tls_init(const struct tls_config *conf) #ifdef CONFIG_FIPS #ifdef OPENSSL_FIPS if (conf && conf->fips_mode) { - if (!FIPS_mode_set(1)) { + static int fips_enabled = 0; + + if (!fips_enabled && !FIPS_mode_set(1)) { wpa_printf(MSG_ERROR, "Failed to enable FIPS " "mode"); ERR_load_crypto_strings(); @@ -756,8 +793,10 @@ void * tls_init(const struct tls_config *conf) os_free(tls_global); tls_global = NULL; return NULL; - } else + } else { wpa_printf(MSG_INFO, "Running in FIPS mode"); + fips_enabled = 1; + } } #else /* OPENSSL_FIPS */ if (conf && conf->fips_mode) { @@ -791,38 +830,58 @@ void * tls_init(const struct tls_config *conf) PKCS12_PBE_add(); #endif /* PKCS12_FUNCS */ } else { -#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA - /* Newer OpenSSL can store app-data per-SSL */ context = tls_context_new(conf); if (context == NULL) return NULL; -#else /* OPENSSL_SUPPORTS_CTX_APP_DATA */ - context = tls_global; -#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ } tls_openssl_ref_count++; - ssl = SSL_CTX_new(SSLv23_method()); + data = os_zalloc(sizeof(*data)); + if (data) + ssl = SSL_CTX_new(SSLv23_method()); + else + ssl = NULL; if (ssl == NULL) { tls_openssl_ref_count--; -#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA if (context != tls_global) os_free(context); -#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ if (tls_openssl_ref_count == 0) { os_free(tls_global); tls_global = NULL; } return NULL; } + data->ssl = ssl; + if (conf) + data->tls_session_lifetime = conf->tls_session_lifetime; SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3); SSL_CTX_set_info_callback(ssl, ssl_info_cb); -#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA SSL_CTX_set_app_data(ssl, context); -#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ + if (data->tls_session_lifetime > 0) { + SSL_CTX_set_quiet_shutdown(ssl, 1); + /* + * Set default context here. In practice, this will be replaced + * by the per-EAP method context in tls_connection_set_verify(). + */ + SSL_CTX_set_session_id_context(ssl, (u8 *) "hostapd", 7); + SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_SERVER); + SSL_CTX_set_timeout(ssl, data->tls_session_lifetime); + SSL_CTX_sess_set_remove_cb(ssl, remove_session_cb); + } else { + SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_OFF); + } + + if (tls_ex_idx_session < 0) { + tls_ex_idx_session = SSL_SESSION_get_ex_new_index( + 0, NULL, NULL, NULL, NULL); + if (tls_ex_idx_session < 0) { + tls_deinit(data); + return NULL; + } + } #ifndef OPENSSL_NO_ENGINE wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); @@ -835,7 +894,7 @@ void * tls_init(const struct tls_config *conf) if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) || tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path, conf->pkcs11_module_path)) { - tls_deinit(ssl); + tls_deinit(data); return NULL; } } @@ -849,22 +908,23 @@ void * tls_init(const struct tls_config *conf) wpa_printf(MSG_ERROR, "OpenSSL: Failed to set cipher string '%s'", ciphers); - tls_deinit(ssl); + tls_deinit(data); return NULL; } - return ssl; + return data; } void tls_deinit(void *ssl_ctx) { - SSL_CTX *ssl = ssl_ctx; -#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + struct tls_data *data = ssl_ctx; + SSL_CTX *ssl = data->ssl; struct tls_context *context = SSL_CTX_get_app_data(ssl); if (context != tls_global) os_free(context); -#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ + if (data->tls_session_lifetime > 0) + SSL_CTX_flush_sessions(ssl, 0); SSL_CTX_free(ssl); tls_openssl_ref_count--; @@ -881,8 +941,31 @@ void tls_deinit(void *ssl_ctx) os_free(tls_global); tls_global = NULL; } + + os_free(data); +} + + +#ifndef OPENSSL_NO_ENGINE + +/* Cryptoki return values */ +#define CKR_PIN_INCORRECT 0x000000a0 +#define CKR_PIN_INVALID 0x000000a1 +#define CKR_PIN_LEN_RANGE 0x000000a2 + +/* libp11 */ +#define ERR_LIB_PKCS11 ERR_LIB_USER + +static int tls_is_pin_error(unsigned int err) +{ + return ERR_GET_LIB(err) == ERR_LIB_PKCS11 && + (ERR_GET_REASON(err) == CKR_PIN_INCORRECT || + ERR_GET_REASON(err) == CKR_PIN_INVALID || + ERR_GET_REASON(err) == CKR_PIN_LEN_RANGE); } +#endif /* OPENSSL_NO_ENGINE */ + static int tls_engine_init(struct tls_connection *conn, const char *engine_id, const char *pin, const char *key_id, @@ -935,11 +1018,16 @@ static int tls_engine_init(struct tls_connection *conn, const char *engine_id, key_id, NULL, &key_cb); if (!conn->private_key) { + unsigned long err = ERR_get_error(); + wpa_printf(MSG_ERROR, "ENGINE: cannot load private key with id '%s' [%s]", key_id, - ERR_error_string(ERR_get_error(), NULL)); - ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + ERR_error_string(err, NULL)); + if (tls_is_pin_error(err)) + ret = TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN; + else + ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; goto err; } } @@ -1030,19 +1118,16 @@ static void tls_msg_cb(int write_p, int version, int content_type, struct tls_connection * tls_connection_init(void *ssl_ctx) { - SSL_CTX *ssl = ssl_ctx; + struct tls_data *data = ssl_ctx; + SSL_CTX *ssl = data->ssl; struct tls_connection *conn; long options; -#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA struct tls_context *context = SSL_CTX_get_app_data(ssl); -#else /* OPENSSL_SUPPORTS_CTX_APP_DATA */ - struct tls_context *context = tls_global; -#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; - conn->ssl_ctx = ssl_ctx; + conn->ssl_ctx = ssl; conn->ssl = SSL_new(ssl); if (conn->ssl == NULL) { tls_show_errors(MSG_INFO, __func__, @@ -1091,6 +1176,14 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return; + if (conn->success_data) { + /* + * Make sure ssl_clear_bad_session() does not remove this + * session. + */ + SSL_set_quiet_shutdown(conn->ssl, 1); + SSL_shutdown(conn->ssl); + } SSL_free(conn->ssl); tls_engine_deinit(conn); os_free(conn->subject_match); @@ -1118,7 +1211,7 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) * and "close notify" shutdown alert would confuse AS. */ SSL_set_quiet_shutdown(conn->ssl, 1); SSL_shutdown(conn->ssl); - return 0; + return SSL_clear(conn->ssl) == 1 ? 0 : -1; } @@ -1617,9 +1710,9 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) #ifndef OPENSSL_NO_STDIO -static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) +static int tls_load_ca_der(struct tls_data *data, const char *ca_cert) { - SSL_CTX *ssl_ctx = _ssl_ctx; + SSL_CTX *ssl_ctx = data->ssl; X509_LOOKUP *lookup; int ret = 0; @@ -1649,11 +1742,12 @@ static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) #endif /* OPENSSL_NO_STDIO */ -static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, +static int tls_connection_ca_cert(struct tls_data *data, + struct tls_connection *conn, const char *ca_cert, const u8 *ca_cert_blob, size_t ca_cert_blob_len, const char *ca_path) { - SSL_CTX *ssl_ctx = _ssl_ctx; + SSL_CTX *ssl_ctx = data->ssl; X509_STORE *store; /* @@ -1788,7 +1882,7 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, tls_show_errors(MSG_WARNING, __func__, "Failed to load root certificates"); if (ca_cert && - tls_load_ca_der(ssl_ctx, ca_cert) == 0) { + tls_load_ca_der(data, ca_cert) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded " "DER format CA certificate", __func__); @@ -1797,7 +1891,7 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, } else { wpa_printf(MSG_DEBUG, "TLS: Trusted root " "certificate(s) loaded"); - tls_get_errors(ssl_ctx); + tls_get_errors(data); } #else /* OPENSSL_NO_STDIO */ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", @@ -1814,8 +1908,10 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, } -static int tls_global_ca_cert(SSL_CTX *ssl_ctx, const char *ca_cert) +static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert) { + SSL_CTX *ssl_ctx = data->ssl; + if (ca_cert) { if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1) { @@ -1843,7 +1939,8 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl) int flags; if (check_crl) { - X509_STORE *cs = SSL_CTX_get_cert_store(ssl_ctx); + struct tls_data *data = ssl_ctx; + X509_STORE *cs = SSL_CTX_get_cert_store(data->ssl); if (cs == NULL) { tls_show_errors(MSG_INFO, __func__, "Failed to get " "certificate store when enabling " @@ -1901,10 +1998,44 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, } +static void tls_set_conn_flags(SSL *ssl, unsigned int flags) +{ +#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 + if (flags & TLS_CONN_DISABLE_TLSv1_0) + SSL_set_options(ssl, SSL_OP_NO_TLSv1); + else + SSL_clear_options(ssl, SSL_OP_NO_TLSv1); +#endif /* SSL_OP_NO_TLSv1 */ +#ifdef SSL_OP_NO_TLSv1_1 + if (flags & TLS_CONN_DISABLE_TLSv1_1) + SSL_set_options(ssl, SSL_OP_NO_TLSv1_1); + else + SSL_clear_options(ssl, SSL_OP_NO_TLSv1_1); +#endif /* SSL_OP_NO_TLSv1_1 */ +#ifdef SSL_OP_NO_TLSv1_2 + if (flags & TLS_CONN_DISABLE_TLSv1_2) + SSL_set_options(ssl, SSL_OP_NO_TLSv1_2); + else + SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2); +#endif /* SSL_OP_NO_TLSv1_2 */ +} + + int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, - int verify_peer) + int verify_peer, unsigned int flags, + const u8 *session_ctx, size_t session_ctx_len) { static int counter = 0; + struct tls_data *data = ssl_ctx; if (conn == NULL) return -1; @@ -1919,20 +2050,25 @@ 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); + conn->flags = flags; + SSL_set_accept_state(conn->ssl); - /* - * Set session id context in order to avoid fatal errors when client - * tries to resume a session. However, set the context to a unique - * value in order to effectively disable session resumption for now - * since not all areas of the server code are ready for it (e.g., - * EAP-TTLS needs special handling for Phase 2 after abbreviated TLS - * handshake). - */ - counter++; - SSL_set_session_id_context(conn->ssl, - (const unsigned char *) &counter, - sizeof(counter)); + if (data->tls_session_lifetime == 0) { + /* + * Set session id context to a unique value to make sure + * session resumption cannot be used either through session + * caching or TLS ticket extension. + */ + counter++; + SSL_set_session_id_context(conn->ssl, + (const unsigned char *) &counter, + sizeof(counter)); + } else if (session_ctx) { + SSL_set_session_id_context(conn->ssl, session_ctx, + session_ctx_len); + } return 0; } @@ -2004,9 +2140,12 @@ static int tls_connection_client_cert(struct tls_connection *conn, } -static int tls_global_client_cert(SSL_CTX *ssl_ctx, const char *client_cert) +static int tls_global_client_cert(struct tls_data *data, + const char *client_cert) { #ifndef OPENSSL_NO_STDIO + SSL_CTX *ssl_ctx = data->ssl; + if (client_cert == NULL) return 0; @@ -2040,7 +2179,7 @@ static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) #ifdef PKCS12_FUNCS -static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, +static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12, const char *passwd) { EVP_PKEY *pkey; @@ -2052,6 +2191,8 @@ static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, pkey = NULL; cert = NULL; certs = NULL; + if (!passwd) + passwd = ""; if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) { tls_show_errors(MSG_DEBUG, __func__, "Failed to parse PKCS12 file"); @@ -2069,7 +2210,7 @@ static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, if (SSL_use_certificate(ssl, cert) != 1) res = -1; } else { - if (SSL_CTX_use_certificate(ssl_ctx, cert) != 1) + if (SSL_CTX_use_certificate(data->ssl, cert) != 1) res = -1; } X509_free(cert); @@ -2081,13 +2222,52 @@ static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, if (SSL_use_PrivateKey(ssl, pkey) != 1) res = -1; } else { - if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1) + if (SSL_CTX_use_PrivateKey(data->ssl, pkey) != 1) res = -1; } EVP_PKEY_free(pkey); } if (certs) { +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_clear_chain_certs(ssl); + while ((cert = sk_X509_pop(certs)) != NULL) { + X509_NAME_oneline(X509_get_subject_name(cert), buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "TLS: additional certificate" + " from PKCS12: subject='%s'", buf); + if (SSL_add1_chain_cert(ssl, cert) != 1) { + tls_show_errors(MSG_DEBUG, __func__, + "Failed to add additional certificate"); + res = -1; + break; + } + } + if (!res) { + /* Try to continue anyway */ + } + sk_X509_free(certs); +#ifndef OPENSSL_IS_BORINGSSL + res = SSL_build_cert_chain(ssl, + SSL_BUILD_CHAIN_FLAG_CHECK | + SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR); + if (!res) { + tls_show_errors(MSG_DEBUG, __func__, + "Failed to build certificate chain"); + } else if (res == 2) { + wpa_printf(MSG_DEBUG, + "TLS: Ignore certificate chain verification error when building chain with PKCS#12 extra certificates"); + } +#endif /* OPENSSL_IS_BORINGSSL */ + /* + * Try to continue regardless of result since it is possible for + * the extra certificates not to be required. + */ + res = 0; +#else /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + SSL_CTX_clear_extra_chain_certs(data->ssl); +#endif /* OPENSSL_VERSION_NUMBER >= 0x10001000L */ while ((cert = sk_X509_pop(certs)) != NULL) { X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); @@ -2097,26 +2277,28 @@ static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, * There is no SSL equivalent for the chain cert - so * always add it to the context... */ - if (SSL_CTX_add_extra_chain_cert(ssl_ctx, cert) != 1) { + if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1) + { res = -1; break; } } sk_X509_free(certs); +#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ } PKCS12_free(p12); if (res < 0) - tls_get_errors(ssl_ctx); + tls_get_errors(data); return res; } #endif /* PKCS12_FUNCS */ -static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, - const char *passwd) +static int tls_read_pkcs12(struct tls_data *data, SSL *ssl, + const char *private_key, const char *passwd) { #ifdef PKCS12_FUNCS FILE *f; @@ -2135,7 +2317,7 @@ static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, return -1; } - return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); + return tls_parse_pkcs12(data, ssl, p12, passwd); #else /* PKCS12_FUNCS */ wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read " @@ -2145,7 +2327,7 @@ static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, } -static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl, +static int tls_read_pkcs12_blob(struct tls_data *data, SSL *ssl, const u8 *blob, size_t len, const char *passwd) { #ifdef PKCS12_FUNCS @@ -2158,7 +2340,7 @@ static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl, return -1; } - return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); + return tls_parse_pkcs12(data, ssl, p12, passwd); #else /* PKCS12_FUNCS */ wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse " @@ -2183,9 +2365,13 @@ static int tls_engine_get_cert(struct tls_connection *conn, if (!ENGINE_ctrl_cmd(conn->engine, "LOAD_CERT_CTRL", 0, ¶ms, NULL, 1)) { + unsigned long err = ERR_get_error(); + wpa_printf(MSG_ERROR, "ENGINE: cannot load client cert with id" " '%s' [%s]", cert_id, - ERR_error_string(ERR_get_error(), NULL)); + ERR_error_string(err, NULL)); + if (tls_is_pin_error(err)) + return TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN; return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; } if (!params.cert) { @@ -2225,13 +2411,13 @@ static int tls_connection_engine_client_cert(struct tls_connection *conn, } -static int tls_connection_engine_ca_cert(void *_ssl_ctx, +static int tls_connection_engine_ca_cert(struct tls_data *data, struct tls_connection *conn, const char *ca_cert_id) { #ifndef OPENSSL_NO_ENGINE X509 *cert; - SSL_CTX *ssl_ctx = _ssl_ctx; + SSL_CTX *ssl_ctx = data->ssl; X509_STORE *store; if (tls_engine_get_cert(conn, ca_cert_id, &cert)) @@ -2297,14 +2483,14 @@ static int tls_connection_engine_private_key(struct tls_connection *conn) } -static int tls_connection_private_key(void *_ssl_ctx, +static int tls_connection_private_key(struct tls_data *data, struct tls_connection *conn, const char *private_key, const char *private_key_passwd, const u8 *private_key_blob, size_t private_key_blob_len) { - SSL_CTX *ssl_ctx = _ssl_ctx; + SSL_CTX *ssl_ctx = data->ssl; char *passwd; int ok; @@ -2350,7 +2536,7 @@ static int tls_connection_private_key(void *_ssl_ctx, break; } - if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob, + if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob, private_key_blob_len, passwd) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> " "OK"); @@ -2383,7 +2569,7 @@ static int tls_connection_private_key(void *_ssl_ctx, __func__); #endif /* OPENSSL_NO_STDIO */ - if (tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd) + if (tls_read_pkcs12(data, conn->ssl, private_key, passwd) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file " "--> OK"); @@ -2422,9 +2608,11 @@ static int tls_connection_private_key(void *_ssl_ctx, } -static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key, +static int tls_global_private_key(struct tls_data *data, + const char *private_key, const char *private_key_passwd) { + SSL_CTX *ssl_ctx = data->ssl; char *passwd; if (private_key == NULL) @@ -2446,7 +2634,7 @@ static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key, SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, SSL_FILETYPE_PEM) != 1 && #endif /* OPENSSL_NO_STDIO */ - tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) { + tls_read_pkcs12(data, NULL, private_key, passwd)) { tls_show_errors(MSG_INFO, __func__, "Failed to load private key"); os_free(passwd); @@ -2541,7 +2729,7 @@ static int tls_connection_dh(struct tls_connection *conn, const char *dh_file) } -static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file) +static int tls_global_dh(struct tls_data *data, const char *dh_file) { #ifdef OPENSSL_NO_DH if (dh_file == NULL) @@ -2550,6 +2738,7 @@ static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file) "dh_file specified"); return -1; #else /* OPENSSL_NO_DH */ + SSL_CTX *ssl_ctx = data->ssl; DH *dh; BIO *bio; @@ -2615,45 +2804,275 @@ static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file) } -int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, - struct tls_keys *keys) +int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, + struct tls_random *keys) { -#ifdef CONFIG_FIPS - wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS " - "mode"); - return -1; -#else /* CONFIG_FIPS */ SSL *ssl; if (conn == NULL || keys == NULL) return -1; ssl = conn->ssl; +#if OPENSSL_VERSION_NUMBER < 0x10100000L if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL) return -1; os_memset(keys, 0, sizeof(*keys)); - keys->master_key = ssl->session->master_key; - keys->master_key_len = ssl->session->master_key_length; keys->client_random = ssl->s3->client_random; keys->client_random_len = SSL3_RANDOM_SIZE; keys->server_random = ssl->s3->server_random; keys->server_random_len = SSL3_RANDOM_SIZE; +#else + if (ssl == NULL) + return -1; + + os_memset(keys, 0, sizeof(*keys)); + keys->client_random = conn->client_random; + keys->client_random_len = SSL_get_client_random( + ssl, conn->client_random, sizeof(conn->client_random)); + keys->server_random = conn->server_random; + keys->server_random_len = SSL_get_server_random( + ssl, conn->server_random, sizeof(conn->server_random)); +#endif return 0; +} + + +#ifndef CONFIG_FIPS +static int openssl_get_keyblock_size(SSL *ssl) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + const EVP_CIPHER *c; + const EVP_MD *h; + int md_size; + + if (ssl->enc_read_ctx == NULL || ssl->enc_read_ctx->cipher == NULL || + ssl->read_hash == NULL) + return -1; + + c = ssl->enc_read_ctx->cipher; +#if OPENSSL_VERSION_NUMBER >= 0x00909000L + h = EVP_MD_CTX_md(ssl->read_hash); +#else + h = ssl->read_hash; +#endif + if (h) + md_size = EVP_MD_size(h); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + else if (ssl->s3) + md_size = ssl->s3->tmp.new_mac_secret_size; +#endif + else + return -1; + + wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d " + "IV_len=%d", EVP_CIPHER_key_length(c), md_size, + EVP_CIPHER_iv_length(c)); + return 2 * (EVP_CIPHER_key_length(c) + + md_size + + EVP_CIPHER_iv_length(c)); +#else + const SSL_CIPHER *ssl_cipher; + int cipher, digest; + const EVP_CIPHER *c; + const EVP_MD *h; + + ssl_cipher = SSL_get_current_cipher(ssl); + if (!ssl_cipher) + return -1; + cipher = SSL_CIPHER_get_cipher_nid(ssl_cipher); + digest = SSL_CIPHER_get_digest_nid(ssl_cipher); + wpa_printf(MSG_DEBUG, "OpenSSL: cipher nid %d digest nid %d", + cipher, digest); + if (cipher < 0 || digest < 0) + return -1; + c = EVP_get_cipherbynid(cipher); + h = EVP_get_digestbynid(digest); + if (!c || !h) + return -1; + + wpa_printf(MSG_DEBUG, + "OpenSSL: keyblock size: key_len=%d MD_size=%d IV_len=%d", + EVP_CIPHER_key_length(c), EVP_MD_size(h), + EVP_CIPHER_iv_length(c)); + return 2 * (EVP_CIPHER_key_length(c) + EVP_MD_size(h) + + EVP_CIPHER_iv_length(c)); +#endif +} +#endif /* CONFIG_FIPS */ + + +static int openssl_tls_prf(struct tls_connection *conn, + const char *label, int server_random_first, + int skip_keyblock, u8 *out, size_t out_len) +{ +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS " + "mode"); + return -1; +#else /* CONFIG_FIPS */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSL *ssl; + u8 *rnd; + int ret = -1; + int skip = 0; + u8 *tmp_out = NULL; + u8 *_out = out; + const char *ver; + + /* + * TLS library did not support key generation, so get the needed TLS + * session parameters and use an internal implementation of TLS PRF to + * derive the key. + */ + + if (conn == NULL) + return -1; + ssl = conn->ssl; + if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL || + ssl->session->master_key_length <= 0) + return -1; + ver = SSL_get_version(ssl); + + if (skip_keyblock) { + skip = openssl_get_keyblock_size(ssl); + if (skip < 0) + return -1; + tmp_out = os_malloc(skip + out_len); + if (!tmp_out) + return -1; + _out = tmp_out; + } + + rnd = os_malloc(2 * SSL3_RANDOM_SIZE); + if (!rnd) { + os_free(tmp_out); + return -1; + } + + if (server_random_first) { + os_memcpy(rnd, ssl->s3->server_random, SSL3_RANDOM_SIZE); + os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->client_random, + SSL3_RANDOM_SIZE); + } else { + os_memcpy(rnd, ssl->s3->client_random, SSL3_RANDOM_SIZE); + os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->server_random, + SSL3_RANDOM_SIZE); + } + + if (os_strcmp(ver, "TLSv1.2") == 0) { + tls_prf_sha256(ssl->session->master_key, + ssl->session->master_key_length, + label, rnd, 2 * SSL3_RANDOM_SIZE, + _out, skip + out_len); + ret = 0; + } else if (tls_prf_sha1_md5(ssl->session->master_key, + ssl->session->master_key_length, + label, rnd, 2 * SSL3_RANDOM_SIZE, + _out, skip + out_len) == 0) { + ret = 0; + } + os_free(rnd); + if (ret == 0 && skip_keyblock) + os_memcpy(out, _out + skip, out_len); + bin_clear_free(tmp_out, skip); + + return ret; +#else + SSL *ssl; + SSL_SESSION *sess; + u8 *rnd; + int ret = -1; + int skip = 0; + u8 *tmp_out = NULL; + u8 *_out = out; + unsigned char client_random[SSL3_RANDOM_SIZE]; + unsigned char server_random[SSL3_RANDOM_SIZE]; + unsigned char master_key[64]; + size_t master_key_len; + const char *ver; + + /* + * TLS library did not support key generation, so get the needed TLS + * session parameters and use an internal implementation of TLS PRF to + * derive the key. + */ + + if (conn == NULL) + return -1; + ssl = conn->ssl; + if (ssl == NULL) + return -1; + ver = SSL_get_version(ssl); + sess = SSL_get_session(ssl); + if (!ver || !sess) + return -1; + + if (skip_keyblock) { + skip = openssl_get_keyblock_size(ssl); + if (skip < 0) + return -1; + tmp_out = os_malloc(skip + out_len); + if (!tmp_out) + return -1; + _out = tmp_out; + } + + rnd = os_malloc(2 * SSL3_RANDOM_SIZE); + if (!rnd) { + os_free(tmp_out); + return -1; + } + + SSL_get_client_random(ssl, client_random, sizeof(client_random)); + SSL_get_server_random(ssl, server_random, sizeof(server_random)); + master_key_len = SSL_SESSION_get_master_key(sess, master_key, + sizeof(master_key)); + + if (server_random_first) { + os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE); + os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random, + SSL3_RANDOM_SIZE); + } else { + os_memcpy(rnd, client_random, SSL3_RANDOM_SIZE); + os_memcpy(rnd + SSL3_RANDOM_SIZE, server_random, + SSL3_RANDOM_SIZE); + } + + if (os_strcmp(ver, "TLSv1.2") == 0) { + tls_prf_sha256(master_key, master_key_len, + label, rnd, 2 * SSL3_RANDOM_SIZE, + _out, skip + out_len); + ret = 0; + } else if (tls_prf_sha1_md5(master_key, master_key_len, + label, rnd, 2 * SSL3_RANDOM_SIZE, + _out, skip + out_len) == 0) { + ret = 0; + } + os_memset(master_key, 0, sizeof(master_key)); + os_free(rnd); + if (ret == 0 && skip_keyblock) + os_memcpy(out, _out + skip, out_len); + bin_clear_free(tmp_out, skip); + + return ret; +#endif #endif /* CONFIG_FIPS */ } int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, - u8 *out, size_t out_len) + int skip_keyblock, u8 *out, size_t out_len) { #if OPENSSL_VERSION_NUMBER >= 0x10001000L SSL *ssl; if (conn == NULL) return -1; - if (server_random_first) - return -1; + if (server_random_first || skip_keyblock) + return openssl_tls_prf(conn, label, + server_random_first, skip_keyblock, + out, out_len); ssl = conn->ssl; if (SSL_export_keying_material(ssl, out, out_len, label, os_strlen(label), NULL, 0, 0) == 1) { @@ -2661,7 +3080,8 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, return 0; } #endif - return -1; + return openssl_tls_prf(conn, label, server_random_first, + skip_keyblock, out, out_len); } @@ -2676,7 +3096,7 @@ openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, * Give TLS handshake data from the server (if available) to OpenSSL * for processing. */ - if (in_data && + if (in_data && wpabuf_len(in_data) > 0 && BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data)) < 0) { tls_show_errors(MSG_INFO, __func__, @@ -2788,8 +3208,14 @@ openssl_connection_handshake(struct tls_connection *conn, return NULL; } - if (SSL_is_init_finished(conn->ssl) && appl_data && in_data) - *appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data)); + if (SSL_is_init_finished(conn->ssl)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Handshake finished - resumed=%d", + tls_connection_resumed(conn->ssl_ctx, conn)); + if (appl_data && in_data) + *appl_data = openssl_get_appl_data(conn, + wpabuf_len(in_data)); + } if (conn->invalid_hb_used) { wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response"); @@ -2968,6 +3394,21 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) + if (os_strstr(buf, ":ADH-")) { + /* + * Need to drop to security level 0 to allow anonymous + * cipher suites for EAP-FAST. + */ + SSL_set_security_level(conn->ssl, 0); + } else if (SSL_get_security_level(conn->ssl) == 0) { + /* Force at least security level 1 */ + SSL_set_security_level(conn->ssl, 1); + } +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ +#endif + if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) { tls_show_errors(MSG_INFO, __func__, "Cipher suite configuration failed"); @@ -2978,6 +3419,22 @@ 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) +{ + const char *name; + if (conn == NULL || conn->ssl == NULL) + return -1; + + name = SSL_get_version(conn->ssl); + if (name == NULL) + return -1; + + os_strlcpy(buf, name, buflen); + return 0; +} + + int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { @@ -3300,6 +3757,7 @@ static int ocsp_status_cb(SSL *s, void *arg) int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { + struct tls_data *data = tls_ctx; int ret; unsigned long err; int can_pkcs11 = 0; @@ -3341,6 +3799,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (can_pkcs11 == 2 && !engine_id) engine_id = "pkcs11"; +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) +#if OPENSSL_VERSION_NUMBER < 0x10100000L if (params->flags & TLS_CONN_EAP_FAST) { wpa_printf(MSG_DEBUG, "OpenSSL: Use TLSv1_method() for EAP-FAST"); @@ -3350,6 +3810,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } } +#endif +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ while ((err = ERR_get_error())) { wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s", @@ -3371,10 +3833,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; if (engine_id && ca_cert_id) { - if (tls_connection_engine_ca_cert(tls_ctx, conn, - ca_cert_id)) + if (tls_connection_engine_ca_cert(data, conn, ca_cert_id)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; - } else if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert, + } else if (tls_connection_ca_cert(data, conn, params->ca_cert, params->ca_cert_blob, params->ca_cert_blob_len, params->ca_path)) @@ -3392,7 +3853,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, wpa_printf(MSG_DEBUG, "TLS: Using private key from engine"); if (tls_connection_engine_private_key(conn)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; - } else if (tls_connection_private_key(tls_ctx, conn, + } else if (tls_connection_private_key(data, conn, params->private_key, params->private_key_passwd, params->private_key_blob, @@ -3416,40 +3877,30 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } -#ifdef SSL_OP_NO_TICKET - if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) - SSL_set_options(conn->ssl, SSL_OP_NO_TICKET); -#ifdef SSL_clear_options - else - SSL_clear_options(conn->ssl, SSL_OP_NO_TICKET); -#endif /* SSL_clear_options */ -#endif /* SSL_OP_NO_TICKET */ - -#ifdef SSL_OP_NO_TLSv1_1 - if (params->flags & TLS_CONN_DISABLE_TLSv1_1) - SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_1); - else - SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_1); -#endif /* SSL_OP_NO_TLSv1_1 */ -#ifdef SSL_OP_NO_TLSv1_2 - if (params->flags & TLS_CONN_DISABLE_TLSv1_2) - SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_2); - else - SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_2); -#endif /* SSL_OP_NO_TLSv1_2 */ + tls_set_conn_flags(conn->ssl, params->flags); #ifdef HAVE_OCSP if (params->flags & TLS_CONN_REQUEST_OCSP) { - SSL_CTX *ssl_ctx = tls_ctx; + SSL_CTX *ssl_ctx = data->ssl; SSL_set_tlsext_status_type(conn->ssl, TLSEXT_STATUSTYPE_ocsp); SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb); SSL_CTX_set_tlsext_status_arg(ssl_ctx, conn); } +#else /* HAVE_OCSP */ + if (params->flags & TLS_CONN_REQUIRE_OCSP) { + wpa_printf(MSG_INFO, + "OpenSSL: No OCSP support included - reject configuration"); + return -1; + } + if (params->flags & TLS_CONN_REQUEST_OCSP) { + wpa_printf(MSG_DEBUG, + "OpenSSL: No OCSP support included - allow optional OCSP case to continue"); + } #endif /* HAVE_OCSP */ conn->flags = params->flags; - tls_get_errors(tls_ctx); + tls_get_errors(data); return 0; } @@ -3458,7 +3909,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, int tls_global_set_params(void *tls_ctx, const struct tls_connection_params *params) { - SSL_CTX *ssl_ctx = tls_ctx; + struct tls_data *data = tls_ctx; + SSL_CTX *ssl_ctx = data->ssl; unsigned long err; while ((err = ERR_get_error())) { @@ -3466,19 +3918,12 @@ int tls_global_set_params(void *tls_ctx, __func__, ERR_error_string(err, NULL)); } - if (tls_global_ca_cert(ssl_ctx, params->ca_cert)) - return -1; - - if (tls_global_client_cert(ssl_ctx, params->client_cert)) - return -1; - - if (tls_global_private_key(ssl_ctx, params->private_key, - params->private_key_passwd)) - return -1; - - if (tls_global_dh(ssl_ctx, params->dh_file)) { - wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'", - params->dh_file); + if (tls_global_ca_cert(data, params->ca_cert) || + tls_global_client_cert(data, params->client_cert) || + tls_global_private_key(data, params->private_key, + params->private_key_passwd) || + tls_global_dh(data, params->dh_file)) { + wpa_printf(MSG_INFO, "TLS: Failed to set global parameters"); return -1; } @@ -3514,49 +3959,6 @@ int tls_global_set_params(void *tls_ctx, } -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn) -{ - const EVP_CIPHER *c; - const EVP_MD *h; - int md_size; - - if (conn == NULL || conn->ssl == NULL || - conn->ssl->enc_read_ctx == NULL || - conn->ssl->enc_read_ctx->cipher == NULL || - conn->ssl->read_hash == NULL) - return -1; - - c = conn->ssl->enc_read_ctx->cipher; -#if OPENSSL_VERSION_NUMBER >= 0x00909000L - h = EVP_MD_CTX_md(conn->ssl->read_hash); -#else - h = conn->ssl->read_hash; -#endif - if (h) - md_size = EVP_MD_size(h); -#if OPENSSL_VERSION_NUMBER >= 0x10000000L - else if (conn->ssl->s3) - md_size = conn->ssl->s3->tmp.new_mac_secret_size; -#endif - else - return -1; - - wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d " - "IV_len=%d", EVP_CIPHER_key_length(c), md_size, - EVP_CIPHER_iv_length(c)); - return 2 * (EVP_CIPHER_key_length(c) + - md_size + - EVP_CIPHER_iv_length(c)); -} - - -unsigned int tls_capabilities(void *tls_ctx) -{ - return 0; -} - - #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) /* Pre-shared secred requires a patch to openssl, so this function is * commented out unless explicitly needed for EAP-FAST in order to be able to @@ -3575,6 +3977,7 @@ 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 if (conn == NULL || conn->session_ticket_cb == NULL) return 0; @@ -3583,6 +3986,23 @@ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, conn->session_ticket_len, s->s3->client_random, s->s3->server_random, secret); +#else + unsigned char client_random[SSL3_RANDOM_SIZE]; + unsigned char server_random[SSL3_RANDOM_SIZE]; + + if (conn == NULL || conn->session_ticket_cb == NULL) + return 0; + + SSL_get_client_random(s, client_random, sizeof(client_random)); + SSL_get_server_random(s, server_random, sizeof(server_random)); + + ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx, + conn->session_ticket, + conn->session_ticket_len, + client_random, + server_random, secret); +#endif + os_free(conn->session_ticket); conn->session_ticket = NULL; @@ -3656,3 +4076,70 @@ int tls_get_library_version(char *buf, size_t buf_len) OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); } + + +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) +{ + SSL_SESSION *sess; + struct wpabuf *old; + + if (tls_ex_idx_session < 0) + goto fail; + sess = SSL_get_session(conn->ssl); + if (!sess) + goto fail; + old = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + if (old) { + wpa_printf(MSG_DEBUG, "OpenSSL: Replacing old success data %p", + old); + wpabuf_free(old); + } + if (SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1) + goto fail; + + wpa_printf(MSG_DEBUG, "OpenSSL: Stored success data %p", data); + conn->success_data = 1; + return; + +fail: + wpa_printf(MSG_INFO, "OpenSSL: Failed to store success data"); + wpabuf_free(data); +} + + +void tls_connection_set_success_data_resumed(struct tls_connection *conn) +{ + wpa_printf(MSG_DEBUG, + "OpenSSL: Success data accepted for resumed session"); + conn->success_data = 1; +} + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + SSL_SESSION *sess; + + if (tls_ex_idx_session < 0 || + !(sess = SSL_get_session(conn->ssl))) + return NULL; + return SSL_SESSION_get_ex_data(sess, tls_ex_idx_session); +} + + +void tls_connection_remove_session(struct tls_connection *conn) +{ + SSL_SESSION *sess; + + sess = SSL_get_session(conn->ssl); + if (!sess) + return; + + if (SSL_CTX_remove_session(conn->ssl_ctx, sess) != 1) + wpa_printf(MSG_DEBUG, + "OpenSSL: Session was not cached"); + else + wpa_printf(MSG_DEBUG, + "OpenSSL: Removed cached session to disable session resumption"); +} diff --git a/src/crypto/tls_schannel.c b/src/crypto/tls_schannel.c deleted file mode 100644 index 31a2c946d0478..0000000000000 --- a/src/crypto/tls_schannel.c +++ /dev/null @@ -1,763 +0,0 @@ -/* - * SSL/TLS interface functions for Microsoft Schannel - * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -/* - * FIX: Go through all SSPI functions and verify what needs to be freed - * FIX: session resumption - * TODO: add support for server cert chain validation - * TODO: add support for CA cert validation - * TODO: add support for EAP-TLS (client cert/key conf) - */ - -#include "includes.h" -#include <windows.h> -#include <wincrypt.h> -#include <schannel.h> -#define SECURITY_WIN32 -#include <security.h> -#include <sspi.h> - -#include "common.h" -#include "tls.h" - - -struct tls_global { - HMODULE hsecurity; - PSecurityFunctionTable sspi; - HCERTSTORE my_cert_store; -}; - -struct tls_connection { - int established, start; - int failed, read_alerts, write_alerts; - - SCHANNEL_CRED schannel_cred; - CredHandle creds; - CtxtHandle context; - - u8 eap_tls_prf[128]; - int eap_tls_prf_set; -}; - - -static int schannel_load_lib(struct tls_global *global) -{ - INIT_SECURITY_INTERFACE pInitSecurityInterface; - - global->hsecurity = LoadLibrary(TEXT("Secur32.dll")); - if (global->hsecurity == NULL) { - wpa_printf(MSG_ERROR, "%s: Could not load Secur32.dll - 0x%x", - __func__, (unsigned int) GetLastError()); - return -1; - } - - pInitSecurityInterface = (INIT_SECURITY_INTERFACE) GetProcAddress( - global->hsecurity, "InitSecurityInterfaceA"); - if (pInitSecurityInterface == NULL) { - wpa_printf(MSG_ERROR, "%s: Could not find " - "InitSecurityInterfaceA from Secur32.dll", - __func__); - FreeLibrary(global->hsecurity); - global->hsecurity = NULL; - return -1; - } - - global->sspi = pInitSecurityInterface(); - if (global->sspi == NULL) { - wpa_printf(MSG_ERROR, "%s: Could not read security " - "interface - 0x%x", - __func__, (unsigned int) GetLastError()); - FreeLibrary(global->hsecurity); - global->hsecurity = NULL; - return -1; - } - - return 0; -} - - -void * tls_init(const struct tls_config *conf) -{ - struct tls_global *global; - - global = os_zalloc(sizeof(*global)); - if (global == NULL) - return NULL; - if (schannel_load_lib(global)) { - os_free(global); - return NULL; - } - return global; -} - - -void tls_deinit(void *ssl_ctx) -{ - struct tls_global *global = ssl_ctx; - - if (global->my_cert_store) - CertCloseStore(global->my_cert_store, 0); - FreeLibrary(global->hsecurity); - os_free(global); -} - - -int tls_get_errors(void *ssl_ctx) -{ - return 0; -} - - -struct tls_connection * tls_connection_init(void *ssl_ctx) -{ - struct tls_connection *conn; - - conn = os_zalloc(sizeof(*conn)); - if (conn == NULL) - return NULL; - conn->start = 1; - - return conn; -} - - -void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return; - - os_free(conn); -} - - -int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) -{ - return conn ? conn->established : 0; -} - - -int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) -{ - struct tls_global *global = ssl_ctx; - if (conn == NULL) - return -1; - - conn->eap_tls_prf_set = 0; - conn->established = conn->failed = 0; - conn->read_alerts = conn->write_alerts = 0; - global->sspi->DeleteSecurityContext(&conn->context); - /* FIX: what else needs to be reseted? */ - - return 0; -} - - -int tls_global_set_params(void *tls_ctx, - const struct tls_connection_params *params) -{ - return -1; -} - - -int tls_global_set_verify(void *ssl_ctx, int check_crl) -{ - return -1; -} - - -int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, - int verify_peer) -{ - return -1; -} - - -int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, - struct tls_keys *keys) -{ - /* Schannel does not export master secret or client/server random. */ - return -1; -} - - -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - u8 *out, size_t out_len) -{ - /* - * Cannot get master_key from Schannel, but EapKeyBlock can be used to - * generate session keys for EAP-TLS and EAP-PEAPv0. EAP-PEAPv2 and - * EAP-TTLS cannot use this, though, since they are using different - * labels. The only option could be to implement TLSv1 completely here - * and just use Schannel or CryptoAPI for low-level crypto - * functionality.. - */ - - if (conn == NULL || !conn->eap_tls_prf_set || server_random_first || - os_strcmp(label, "client EAP encryption") != 0 || - out_len > sizeof(conn->eap_tls_prf)) - return -1; - - os_memcpy(out, conn->eap_tls_prf, out_len); - - return 0; -} - - -static struct wpabuf * tls_conn_hs_clienthello(struct tls_global *global, - struct tls_connection *conn) -{ - DWORD sspi_flags, sspi_flags_out; - SecBufferDesc outbuf; - SecBuffer outbufs[1]; - SECURITY_STATUS status; - TimeStamp ts_expiry; - - sspi_flags = ISC_REQ_REPLAY_DETECT | - ISC_REQ_CONFIDENTIALITY | - ISC_RET_EXTENDED_ERROR | - ISC_REQ_ALLOCATE_MEMORY | - ISC_REQ_MANUAL_CRED_VALIDATION; - - wpa_printf(MSG_DEBUG, "%s: Generating ClientHello", __func__); - - outbufs[0].pvBuffer = NULL; - outbufs[0].BufferType = SECBUFFER_TOKEN; - outbufs[0].cbBuffer = 0; - - outbuf.cBuffers = 1; - outbuf.pBuffers = outbufs; - outbuf.ulVersion = SECBUFFER_VERSION; - -#ifdef UNICODE - status = global->sspi->InitializeSecurityContextW( - &conn->creds, NULL, NULL /* server name */, sspi_flags, 0, - SECURITY_NATIVE_DREP, NULL, 0, &conn->context, - &outbuf, &sspi_flags_out, &ts_expiry); -#else /* UNICODE */ - status = global->sspi->InitializeSecurityContextA( - &conn->creds, NULL, NULL /* server name */, sspi_flags, 0, - SECURITY_NATIVE_DREP, NULL, 0, &conn->context, - &outbuf, &sspi_flags_out, &ts_expiry); -#endif /* UNICODE */ - if (status != SEC_I_CONTINUE_NEEDED) { - wpa_printf(MSG_ERROR, "%s: InitializeSecurityContextA " - "failed - 0x%x", - __func__, (unsigned int) status); - return NULL; - } - - if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { - struct wpabuf *buf; - wpa_hexdump(MSG_MSGDUMP, "SChannel - ClientHello", - outbufs[0].pvBuffer, outbufs[0].cbBuffer); - conn->start = 0; - buf = wpabuf_alloc_copy(outbufs[0].pvBuffer, - outbufs[0].cbBuffer); - if (buf == NULL) - return NULL; - global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); - return buf; - } - - wpa_printf(MSG_ERROR, "SChannel: Failed to generate ClientHello"); - - return NULL; -} - - -#ifndef SECPKG_ATTR_EAP_KEY_BLOCK -#define SECPKG_ATTR_EAP_KEY_BLOCK 0x5b - -typedef struct _SecPkgContext_EapKeyBlock { - BYTE rgbKeys[128]; - BYTE rgbIVs[64]; -} SecPkgContext_EapKeyBlock, *PSecPkgContext_EapKeyBlock; -#endif /* !SECPKG_ATTR_EAP_KEY_BLOCK */ - -static int tls_get_eap(struct tls_global *global, struct tls_connection *conn) -{ - SECURITY_STATUS status; - SecPkgContext_EapKeyBlock kb; - - /* Note: Windows NT and Windows Me/98/95 do not support getting - * EapKeyBlock */ - - status = global->sspi->QueryContextAttributes( - &conn->context, SECPKG_ATTR_EAP_KEY_BLOCK, &kb); - if (status != SEC_E_OK) { - wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes(" - "SECPKG_ATTR_EAP_KEY_BLOCK) failed (%d)", - __func__, (int) status); - return -1; - } - - wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbKeys", - kb.rgbKeys, sizeof(kb.rgbKeys)); - wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbIVs", - kb.rgbIVs, sizeof(kb.rgbIVs)); - - os_memcpy(conn->eap_tls_prf, kb.rgbKeys, sizeof(kb.rgbKeys)); - conn->eap_tls_prf_set = 1; - return 0; -} - - -struct wpabuf * tls_connection_handshake(void *tls_ctx, - struct tls_connection *conn, - const struct wpabuf *in_data, - struct wpabuf **appl_data) -{ - struct tls_global *global = tls_ctx; - DWORD sspi_flags, sspi_flags_out; - SecBufferDesc inbuf, outbuf; - SecBuffer inbufs[2], outbufs[1]; - SECURITY_STATUS status; - TimeStamp ts_expiry; - struct wpabuf *out_buf = NULL; - - if (appl_data) - *appl_data = NULL; - - if (conn->start) - return tls_conn_hs_clienthello(global, conn); - - wpa_printf(MSG_DEBUG, "SChannel: %d bytes handshake data to process", - (int) wpabuf_len(in_data)); - - sspi_flags = ISC_REQ_REPLAY_DETECT | - ISC_REQ_CONFIDENTIALITY | - ISC_RET_EXTENDED_ERROR | - ISC_REQ_ALLOCATE_MEMORY | - ISC_REQ_MANUAL_CRED_VALIDATION; - - /* Input buffer for Schannel */ - inbufs[0].pvBuffer = (u8 *) wpabuf_head(in_data); - inbufs[0].cbBuffer = wpabuf_len(in_data); - inbufs[0].BufferType = SECBUFFER_TOKEN; - - /* Place for leftover data from Schannel */ - inbufs[1].pvBuffer = NULL; - inbufs[1].cbBuffer = 0; - inbufs[1].BufferType = SECBUFFER_EMPTY; - - inbuf.cBuffers = 2; - inbuf.pBuffers = inbufs; - inbuf.ulVersion = SECBUFFER_VERSION; - - /* Output buffer for Schannel */ - outbufs[0].pvBuffer = NULL; - outbufs[0].cbBuffer = 0; - outbufs[0].BufferType = SECBUFFER_TOKEN; - - outbuf.cBuffers = 1; - outbuf.pBuffers = outbufs; - outbuf.ulVersion = SECBUFFER_VERSION; - -#ifdef UNICODE - status = global->sspi->InitializeSecurityContextW( - &conn->creds, &conn->context, NULL, sspi_flags, 0, - SECURITY_NATIVE_DREP, &inbuf, 0, NULL, - &outbuf, &sspi_flags_out, &ts_expiry); -#else /* UNICODE */ - status = global->sspi->InitializeSecurityContextA( - &conn->creds, &conn->context, NULL, sspi_flags, 0, - SECURITY_NATIVE_DREP, &inbuf, 0, NULL, - &outbuf, &sspi_flags_out, &ts_expiry); -#endif /* UNICODE */ - - wpa_printf(MSG_MSGDUMP, "Schannel: InitializeSecurityContext -> " - "status=%d inlen[0]=%d intype[0]=%d inlen[1]=%d " - "intype[1]=%d outlen[0]=%d", - (int) status, (int) inbufs[0].cbBuffer, - (int) inbufs[0].BufferType, (int) inbufs[1].cbBuffer, - (int) inbufs[1].BufferType, - (int) outbufs[0].cbBuffer); - if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED || - (FAILED(status) && (sspi_flags_out & ISC_RET_EXTENDED_ERROR))) { - if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { - wpa_hexdump(MSG_MSGDUMP, "SChannel - output", - outbufs[0].pvBuffer, outbufs[0].cbBuffer); - out_buf = wpabuf_alloc_copy(outbufs[0].pvBuffer, - outbufs[0].cbBuffer); - global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); - outbufs[0].pvBuffer = NULL; - if (out_buf == NULL) - return NULL; - } - } - - switch (status) { - case SEC_E_INCOMPLETE_MESSAGE: - wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INCOMPLETE_MESSAGE"); - break; - case SEC_I_CONTINUE_NEEDED: - wpa_printf(MSG_DEBUG, "Schannel: SEC_I_CONTINUE_NEEDED"); - break; - case SEC_E_OK: - /* TODO: verify server certificate chain */ - wpa_printf(MSG_DEBUG, "Schannel: SEC_E_OK - Handshake " - "completed successfully"); - conn->established = 1; - tls_get_eap(global, conn); - - /* Need to return something to get final TLS ACK. */ - if (out_buf == NULL) - out_buf = wpabuf_alloc(0); - - if (inbufs[1].BufferType == SECBUFFER_EXTRA) { - wpa_hexdump(MSG_MSGDUMP, "SChannel - Encrypted " - "application data", - inbufs[1].pvBuffer, inbufs[1].cbBuffer); - if (appl_data) { - *appl_data = wpabuf_alloc_copy( - outbufs[1].pvBuffer, - outbufs[1].cbBuffer); - } - global->sspi->FreeContextBuffer(inbufs[1].pvBuffer); - inbufs[1].pvBuffer = NULL; - } - break; - case SEC_I_INCOMPLETE_CREDENTIALS: - wpa_printf(MSG_DEBUG, - "Schannel: SEC_I_INCOMPLETE_CREDENTIALS"); - break; - case SEC_E_WRONG_PRINCIPAL: - wpa_printf(MSG_DEBUG, "Schannel: SEC_E_WRONG_PRINCIPAL"); - break; - case SEC_E_INTERNAL_ERROR: - wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INTERNAL_ERROR"); - break; - } - - if (FAILED(status)) { - wpa_printf(MSG_DEBUG, "Schannel: Handshake failed " - "(out_buf=%p)", out_buf); - conn->failed++; - global->sspi->DeleteSecurityContext(&conn->context); - return out_buf; - } - - if (inbufs[1].BufferType == SECBUFFER_EXTRA) { - /* TODO: Can this happen? What to do with this data? */ - wpa_hexdump(MSG_MSGDUMP, "SChannel - Leftover data", - inbufs[1].pvBuffer, inbufs[1].cbBuffer); - global->sspi->FreeContextBuffer(inbufs[1].pvBuffer); - inbufs[1].pvBuffer = NULL; - } - - return out_buf; -} - - -struct wpabuf * tls_connection_server_handshake(void *tls_ctx, - struct tls_connection *conn, - const struct wpabuf *in_data, - struct wpabuf **appl_data) -{ - return NULL; -} - - -struct wpabuf * tls_connection_encrypt(void *tls_ctx, - struct tls_connection *conn, - const struct wpabuf *in_data) -{ - struct tls_global *global = tls_ctx; - SECURITY_STATUS status; - SecBufferDesc buf; - SecBuffer bufs[4]; - SecPkgContext_StreamSizes sizes; - int i; - struct wpabuf *out; - - status = global->sspi->QueryContextAttributes(&conn->context, - SECPKG_ATTR_STREAM_SIZES, - &sizes); - if (status != SEC_E_OK) { - wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes failed", - __func__); - return NULL; - } - wpa_printf(MSG_DEBUG, "%s: Stream sizes: header=%u trailer=%u", - __func__, - (unsigned int) sizes.cbHeader, - (unsigned int) sizes.cbTrailer); - - out = wpabuf_alloc(sizes.cbHeader + wpabuf_len(in_data) + - sizes.cbTrailer); - - os_memset(&bufs, 0, sizeof(bufs)); - bufs[0].pvBuffer = wpabuf_put(out, sizes.cbHeader); - bufs[0].cbBuffer = sizes.cbHeader; - bufs[0].BufferType = SECBUFFER_STREAM_HEADER; - - bufs[1].pvBuffer = wpabuf_put(out, 0); - wpabuf_put_buf(out, in_data); - bufs[1].cbBuffer = wpabuf_len(in_data); - bufs[1].BufferType = SECBUFFER_DATA; - - bufs[2].pvBuffer = wpabuf_put(out, sizes.cbTrailer); - bufs[2].cbBuffer = sizes.cbTrailer; - bufs[2].BufferType = SECBUFFER_STREAM_TRAILER; - - buf.ulVersion = SECBUFFER_VERSION; - buf.cBuffers = 3; - buf.pBuffers = bufs; - - status = global->sspi->EncryptMessage(&conn->context, 0, &buf, 0); - - wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage -> " - "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d " - "len[2]=%d type[2]=%d", - (int) status, - (int) bufs[0].cbBuffer, (int) bufs[0].BufferType, - (int) bufs[1].cbBuffer, (int) bufs[1].BufferType, - (int) bufs[2].cbBuffer, (int) bufs[2].BufferType); - wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage pointers: " - "out_data=%p bufs %p %p %p", - wpabuf_head(out), bufs[0].pvBuffer, bufs[1].pvBuffer, - bufs[2].pvBuffer); - - for (i = 0; i < 3; i++) { - if (bufs[i].pvBuffer && bufs[i].BufferType != SECBUFFER_EMPTY) - { - wpa_hexdump(MSG_MSGDUMP, "SChannel: bufs", - bufs[i].pvBuffer, bufs[i].cbBuffer); - } - } - - if (status == SEC_E_OK) { - wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__); - wpa_hexdump_buf_key(MSG_MSGDUMP, "Schannel: Encrypted data " - "from EncryptMessage", out); - return out; - } - - wpa_printf(MSG_DEBUG, "%s: Failed - status=%d", - __func__, (int) status); - wpabuf_free(out); - return NULL; -} - - -struct wpabuf * tls_connection_decrypt(void *tls_ctx, - struct tls_connection *conn, - const struct wpabuf *in_data) -{ - struct tls_global *global = tls_ctx; - SECURITY_STATUS status; - SecBufferDesc buf; - SecBuffer bufs[4]; - int i; - struct wpabuf *out, *tmp; - - wpa_hexdump_buf(MSG_MSGDUMP, - "Schannel: Encrypted data to DecryptMessage", in_data); - os_memset(&bufs, 0, sizeof(bufs)); - tmp = wpabuf_dup(in_data); - if (tmp == NULL) - return NULL; - bufs[0].pvBuffer = wpabuf_mhead(tmp); - bufs[0].cbBuffer = wpabuf_len(in_data); - bufs[0].BufferType = SECBUFFER_DATA; - - bufs[1].BufferType = SECBUFFER_EMPTY; - bufs[2].BufferType = SECBUFFER_EMPTY; - bufs[3].BufferType = SECBUFFER_EMPTY; - - buf.ulVersion = SECBUFFER_VERSION; - buf.cBuffers = 4; - buf.pBuffers = bufs; - - status = global->sspi->DecryptMessage(&conn->context, &buf, 0, - NULL); - wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage -> " - "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d " - "len[2]=%d type[2]=%d len[3]=%d type[3]=%d", - (int) status, - (int) bufs[0].cbBuffer, (int) bufs[0].BufferType, - (int) bufs[1].cbBuffer, (int) bufs[1].BufferType, - (int) bufs[2].cbBuffer, (int) bufs[2].BufferType, - (int) bufs[3].cbBuffer, (int) bufs[3].BufferType); - wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage pointers: " - "out_data=%p bufs %p %p %p %p", - wpabuf_head(tmp), bufs[0].pvBuffer, bufs[1].pvBuffer, - bufs[2].pvBuffer, bufs[3].pvBuffer); - - switch (status) { - case SEC_E_INCOMPLETE_MESSAGE: - wpa_printf(MSG_DEBUG, "%s: SEC_E_INCOMPLETE_MESSAGE", - __func__); - break; - case SEC_E_OK: - wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__); - for (i = 0; i < 4; i++) { - if (bufs[i].BufferType == SECBUFFER_DATA) - break; - } - if (i == 4) { - wpa_printf(MSG_DEBUG, "%s: No output data from " - "DecryptMessage", __func__); - wpabuf_free(tmp); - return NULL; - } - wpa_hexdump_key(MSG_MSGDUMP, "Schannel: Decrypted data from " - "DecryptMessage", - bufs[i].pvBuffer, bufs[i].cbBuffer); - out = wpabuf_alloc_copy(bufs[i].pvBuffer, bufs[i].cbBuffer); - wpabuf_free(tmp); - return out; - } - - wpa_printf(MSG_DEBUG, "%s: Failed - status=%d", - __func__, (int) status); - wpabuf_free(tmp); - return NULL; -} - - -int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) -{ - return 0; -} - - -int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, - u8 *ciphers) -{ - return -1; -} - - -int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, - char *buf, size_t buflen) -{ - return -1; -} - - -int tls_connection_enable_workaround(void *ssl_ctx, - struct tls_connection *conn) -{ - return 0; -} - - -int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, - int ext_type, const u8 *data, - size_t data_len) -{ - return -1; -} - - -int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return -1; - return conn->failed; -} - - -int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return -1; - return conn->read_alerts; -} - - -int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return -1; - return conn->write_alerts; -} - - -int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, - const struct tls_connection_params *params) -{ - struct tls_global *global = tls_ctx; - ALG_ID algs[1]; - SECURITY_STATUS status; - TimeStamp ts_expiry; - - if (conn == NULL) - return -1; - - if (params->subject_match) { - wpa_printf(MSG_INFO, "TLS: subject_match not supported"); - return -1; - } - - if (params->altsubject_match) { - wpa_printf(MSG_INFO, "TLS: altsubject_match not supported"); - return -1; - } - - if (params->suffix_match) { - wpa_printf(MSG_INFO, "TLS: suffix_match not supported"); - return -1; - } - - if (params->domain_match) { - wpa_printf(MSG_INFO, "TLS: domain_match not supported"); - return -1; - } - - if (params->openssl_ciphers) { - wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported"); - return -1; - } - - if (global->my_cert_store == NULL && - (global->my_cert_store = CertOpenSystemStore(0, TEXT("MY"))) == - NULL) { - wpa_printf(MSG_ERROR, "%s: CertOpenSystemStore failed - 0x%x", - __func__, (unsigned int) GetLastError()); - return -1; - } - - os_memset(&conn->schannel_cred, 0, sizeof(conn->schannel_cred)); - conn->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; - conn->schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1; - algs[0] = CALG_RSA_KEYX; - conn->schannel_cred.cSupportedAlgs = 1; - conn->schannel_cred.palgSupportedAlgs = algs; - conn->schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; -#ifdef UNICODE - status = global->sspi->AcquireCredentialsHandleW( - NULL, UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL, - &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry); -#else /* UNICODE */ - status = global->sspi->AcquireCredentialsHandleA( - NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, - &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry); -#endif /* UNICODE */ - if (status != SEC_E_OK) { - wpa_printf(MSG_DEBUG, "%s: AcquireCredentialsHandleA failed - " - "0x%x", __func__, (unsigned int) status); - return -1; - } - - return 0; -} - - -unsigned int tls_capabilities(void *tls_ctx) -{ - return 0; -} - - -int tls_get_library_version(char *buf, size_t buf_len) -{ - return os_snprintf(buf, buf_len, "schannel"); -} diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 03bd1a79a14c0..3cdab5a7a87d6 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -20,6 +20,7 @@ #define WPA_SUPPLICANT_DRIVER_VERSION 4 #include "common/defs.h" +#include "common/ieee802_11_defs.h" #include "utils/list.h" #define HOSTAPD_CHAN_DISABLED 0x00000001 @@ -341,7 +342,7 @@ struct wpa_driver_scan_params { * is not needed anymore. */ struct wpa_driver_scan_filter { - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; } *filter_ssids; @@ -1211,6 +1212,8 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_HT_IBSS 0x0000001000000000ULL /** Driver supports IBSS with VHT datarates */ #define WPA_DRIVER_FLAGS_VHT_IBSS 0x0000002000000000ULL +/** Driver supports automatic band selection */ +#define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY 0x0000004000000000ULL u64 flags; #define WPA_DRIVER_SMPS_MODE_STATIC 0x00000001 @@ -1294,6 +1297,13 @@ struct wpa_driver_capa { */ #define WPA_DRIVER_FLAGS_TX_POWER_INSERTION 0x00000008 u32 rrm_flags; + + /* Driver concurrency capabilities */ + unsigned int conc_capab; + /* Maximum number of concurrent channels on 2.4 GHz */ + unsigned int max_conc_chan_2_4; + /* Maximum number of concurrent channels on 5 GHz */ + unsigned int max_conc_chan_5_0; }; @@ -1394,6 +1404,16 @@ enum wpa_driver_if_type { * WPA_IF_MESH - Mesh interface */ WPA_IF_MESH, + + /* + * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only) + */ + WPA_IF_TDLS, + + /* + * WPA_IF_IBSS - IBSS interface (used for pref freq only) + */ + WPA_IF_IBSS, }; struct wpa_init_params { @@ -1477,6 +1497,7 @@ struct wpa_signal_info { int above_threshold; int current_signal; int avg_signal; + int avg_beacon_signal; int current_noise; int current_txrate; enum chan_width chanwidth; @@ -1576,6 +1597,7 @@ enum drv_br_port_attr { enum drv_br_net_param { DRV_BR_NET_PARAM_GARP_ACCEPT, + DRV_BR_MULTICAST_SNOOPING, }; struct drv_acs_params { @@ -1587,6 +1609,17 @@ struct drv_acs_params { /* Indicates whether HT40 is enabled */ int ht40_enabled; + + /* Indicates whether VHT is enabled */ + int vht_enabled; + + /* Configured ACS channel width */ + u16 ch_width; + + /* ACS channel list info */ + unsigned int ch_list_len; + const u8 *ch_list; + const int *freq_list; }; @@ -1925,10 +1958,12 @@ struct wpa_driver_ops { * @data: IEEE 802.11 management frame with IEEE 802.11 header * @data_len: Size of the management frame * @noack: Do not wait for this frame to be acked (disable retries) + * @freq: Frequency (in MHz) to send the frame on, or 0 to let the + * driver decide * Returns: 0 on success, -1 on failure */ int (*send_mlme)(void *priv, const u8 *data, size_t data_len, - int noack); + int noack, unsigned int freq); /** * update_ft_ies - Update FT (IEEE 802.11r) IEs @@ -2332,7 +2367,8 @@ struct wpa_driver_ops { * Returns: 0 on success, -1 on failure */ int (*sta_set_flags)(void *priv, const u8 *addr, - int total_flags, int flags_or, int flags_and); + unsigned int total_flags, unsigned int flags_or, + unsigned int flags_and); /** * set_tx_queue_params - Set TX queue parameters @@ -2656,18 +2692,6 @@ struct wpa_driver_ops { int encrypt); /** - * shared_freq - Get operating frequency of shared interface(s) - * @priv: Private driver interface data - * Returns: Operating frequency in MHz, 0 if no shared operation in - * use, or -1 on failure - * - * This command can be used to request the current operating frequency - * of any virtual interface that shares the same radio to provide - * information for channel selection for other virtual interfaces. - */ - int (*shared_freq)(void *priv); - - /** * get_noa - Get current Notice of Absence attribute payload * @priv: Private driver interface data * @buf: Buffer for returning NoA @@ -3381,6 +3405,40 @@ struct wpa_driver_ops { * indicates support for such offloading (WPA_DRIVER_FLAGS_ACS_OFFLOAD). */ int (*do_acs)(void *priv, struct drv_acs_params *params); + + /** + * set_band - Notify driver of band selection + * @priv: Private driver interface data + * @band: The selected band(s) + * Returns 0 on success, -1 on failure + */ + int (*set_band)(void *priv, enum set_band band); + + /** + * get_pref_freq_list - Get preferred frequency list for an interface + * @priv: Private driver interface data + * @if_type: Interface type + * @num: Number of channels + * @freq_list: Preferred channel frequency list encoded in MHz values + * Returns 0 on success, -1 on failure + * + * This command can be used to query the preferred frequency list from + * the driver specific to a particular interface type. + */ + int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type, + unsigned int *num, unsigned int *freq_list); + + /** + * set_prob_oper_freq - Indicate probable P2P operating channel + * @priv: Private driver interface data + * @freq: Channel frequency in MHz + * Returns 0 on success, -1 on failure + * + * This command can be used to inform the driver of the operating + * frequency that an ongoing P2P group formation is likely to come up + * on. Local device is assuming P2P Client role. + */ + int (*set_prob_oper_freq)(void *priv, unsigned int freq); }; @@ -4557,10 +4615,20 @@ union wpa_event_data { * struct acs_selected_channels - Data for EVENT_ACS_CHANNEL_SELECTED * @pri_channel: Selected primary channel * @sec_channel: Selected secondary channel + * @vht_seg0_center_ch: VHT mode Segment0 center channel + * @vht_seg1_center_ch: VHT mode Segment1 center channel + * @ch_width: Selected Channel width by driver. Driver may choose to + * change hostapd configured ACS channel width due driver internal + * channel restrictions. + * hw_mode: Selected band (used with hw_mode=any) */ struct acs_selected_channels { u8 pri_channel; u8 sec_channel; + u8 vht_seg0_center_ch; + u8 vht_seg1_center_ch; + u16 ch_width; + enum hostapd_hw_mode hw_mode; } acs_selected_channels; }; @@ -4631,6 +4699,6 @@ wpa_get_wowlan_triggers(const char *wowlan_triggers, const struct wpa_driver_capa *capa); /* NULL terminated array of linked in driver wrappers */ -extern struct wpa_driver_ops *wpa_drivers[]; +extern const struct wpa_driver_ops *const wpa_drivers[]; #endif /* DRIVER_H */ diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c index f46442163d939..ef140934973af 100644 --- a/src/drivers/driver_atheros.c +++ b/src/drivers/driver_atheros.c @@ -55,6 +55,10 @@ #include "netlink.h" #include "linux_ioctl.h" +#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) || defined(CONFIG_WNM) || defined(CONFIG_WPS) +#define ATHEROS_USE_RAW_RECEIVE +#endif + struct atheros_driver_data { struct hostapd_data *hapd; /* back pointer */ @@ -430,7 +434,8 @@ atheros_set_sta_authorized(void *priv, const u8 *addr, int authorized) static int atheros_sta_set_flags(void *priv, const u8 *addr, - int total_flags, int flags_or, int flags_and) + unsigned int total_flags, unsigned int flags_or, + unsigned int flags_and) { /* For now, only support setting Authorized flag */ if (flags_or & WPA_STA_AUTHORIZED) @@ -823,7 +828,7 @@ static int atheros_set_qos_map(void *ctx, const u8 *qos_map_set, return 0; } -#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM) || defined(CONFIG_HS20) +#ifdef ATHEROS_USE_RAW_RECEIVE static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { @@ -911,7 +916,7 @@ static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, break; } } -#endif +#endif /* ATHEROS_USE_RAW_RECEIVE */ static int atheros_receive_pkt(struct atheros_driver_data *drv) { @@ -923,11 +928,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 */ -#ifdef CONFIG_IEEE80211R +#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ | IEEE80211_FILTER_TYPE_AUTH | IEEE80211_FILTER_TYPE_ACTION); -#endif +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ #ifdef CONFIG_WNM filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; #endif /* CONFIG_WNM */ @@ -1026,7 +1031,7 @@ atheros_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, #define atheros_set_ap_wps_ie NULL #endif /* CONFIG_WPS */ -#ifdef CONFIG_IEEE80211R +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) 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) @@ -1102,7 +1107,7 @@ atheros_sta_assoc(void *priv, const u8 *own_addr, const u8 *addr, } return ret; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ static void atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) @@ -1177,6 +1182,7 @@ static void atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, char *custom, char *end) { +#define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */ wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { @@ -1232,9 +1238,6 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, * so all are enabled for WPS... ugh. */ wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL); -#endif /* CONFIG_WPS */ -#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) -#define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */ } else if (strncmp(custom, "Manage.prob_req ", 16) == 0) { /* * Atheros driver uses a hack to pass Probe Request frames as a @@ -1250,40 +1253,46 @@ 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) } else if (strncmp(custom, "Manage.assoc_req ", 17) == 0) { /* Format: "Manage.assoc_req <frame len>" | zero padding | * frame */ int len = atoi(custom + 17); if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { - wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/" - "assoc_req/auth event length %d", len); + wpa_printf(MSG_DEBUG, + "Invalid Manage.assoc_req event length %d", + len); return; } atheros_raw_receive(drv, NULL, (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); - } else if (strncmp(custom, "Manage.action ", 14) == 0) { - /* Format: "Manage.assoc_req <frame len>" | zero padding | - * frame */ - int len = atoi(custom + 14); - if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { - wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/" - "assoc_req/auth event length %d", len); + } else if (strncmp(custom, "Manage.auth ", 12) == 0) { + /* Format: "Manage.auth <frame len>" | zero padding | frame */ + int len = atoi(custom + 12); + if (len < 0 || + custom + MGMT_FRAM_TAG_SIZE + len > end) { + wpa_printf(MSG_DEBUG, + "Invalid Manage.auth event length %d", len); return; } atheros_raw_receive(drv, NULL, (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); - } else if (strncmp(custom, "Manage.auth ", 12) == 0) { - /* Format: "Manage.auth <frame len>" | zero padding | frame +#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */ +#ifdef ATHEROS_USE_RAW_RECEIVE + } else if (strncmp(custom, "Manage.action ", 14) == 0) { + /* Format: "Manage.assoc_req <frame len>" | zero padding | frame */ - int len = atoi(custom + 12); + int len = atoi(custom + 14); if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { - wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/" - "assoc_req/auth event length %d", len); + wpa_printf(MSG_DEBUG, + "Invalid Manage.action event length %d", + len); return; } atheros_raw_receive(drv, NULL, (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); -#endif /* CONFIG_WPS or CONFIG_IEEE80211R */ +#endif /* ATHEROS_USE_RAW_RECEIVE */ } } @@ -1682,8 +1691,7 @@ bad: l2_packet_deinit(drv->sock_xmit); if (drv->ioctl_sock >= 0) close(drv->ioctl_sock); - if (drv != NULL) - free(drv); + os_free(drv); return NULL; } @@ -1694,6 +1702,13 @@ atheros_deinit(void *priv) struct atheros_driver_data *drv = priv; atheros_reset_appfilter(drv); + + if (drv->wpa_ie || drv->wps_beacon_ie || drv->wps_probe_resp_ie) { + wpabuf_free(drv->wpa_ie); + wpabuf_free(drv->wps_beacon_ie); + wpabuf_free(drv->wps_probe_resp_ie); + atheros_set_opt_ie(priv, NULL, 0); + } netlink_deinit(drv->netlink); (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); if (drv->ioctl_sock >= 0) @@ -1704,10 +1719,7 @@ atheros_deinit(void *priv) l2_packet_deinit(drv->sock_xmit); if (drv->sock_raw) l2_packet_deinit(drv->sock_raw); - wpabuf_free(drv->wpa_ie); - wpabuf_free(drv->wps_beacon_ie); - wpabuf_free(drv->wps_probe_resp_ie); - free(drv); + os_free(drv); } static int @@ -1833,10 +1845,10 @@ static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params) } -#ifdef CONFIG_IEEE80211R +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len, - int noack) + int noack, unsigned int freq) { struct atheros_driver_data *drv = priv; u8 buf[1510]; @@ -1858,8 +1870,11 @@ 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 */ +#ifdef CONFIG_IEEE80211R + static int atheros_add_tspec(void *priv, const u8 *addr, u8 *tspec_ie, size_t tspec_ielen) { @@ -2139,10 +2154,12 @@ 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, -#ifdef CONFIG_IEEE80211R +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) .sta_assoc = atheros_sta_assoc, .sta_auth = atheros_sta_auth, .send_mlme = atheros_send_mgmt, +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211R .add_tspec = atheros_add_tspec, .add_sta_node = atheros_add_sta_node, #endif /* CONFIG_IEEE80211R */ diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c index 0f1a0f60f27d6..bab1f031d24cd 100644 --- a/src/drivers/driver_bsd.c +++ b/src/drivers/driver_bsd.c @@ -861,8 +861,7 @@ bad: if (drv->sock >= 0) close(drv->sock); os_free(drv->event_buf); - if (drv != NULL) - os_free(drv); + os_free(drv); return NULL; } @@ -895,7 +894,8 @@ bsd_commit(void *priv) static int bsd_set_sta_authorized(void *priv, const u8 *addr, - int total_flags, int flags_or, int flags_and) + unsigned int total_flags, unsigned int flags_or, + unsigned int flags_and) { int authorized = -1; diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c index 84b98fb8c87f3..a7aa5eff00bd9 100644 --- a/src/drivers/driver_hostap.c +++ b/src/drivers/driver_hostap.c @@ -140,7 +140,7 @@ static void handle_tx_callback(struct hostap_driver_data *drv, u8 *buf, static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len) { struct ieee80211_hdr *hdr; - u16 fc, extra_len, type, stype; + u16 fc, type, stype; size_t data_len = len; int ver; union wpa_event_data event; @@ -165,19 +165,10 @@ static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len) ver = fc & WLAN_FC_PVER; - /* protocol version 3 is reserved for indicating extra data after the - * payload, version 2 for indicating ACKed frame (TX callbacks), and - * version 1 for indicating failed frame (no ACK, TX callbacks) */ - if (ver == 3) { - u8 *pos = buf + len - 2; - extra_len = WPA_GET_LE16(pos); - printf("extra data in frame (elen=%d)\n", extra_len); - if ((size_t) extra_len + 2 > len) { - printf(" extra data overflow\n"); - return; - } - len -= extra_len + 2; - } else if (ver == 1 || ver == 2) { + /* protocol version 2 is reserved for indicating ACKed frame (TX + * callbacks), and version 1 for indicating failed frame (no ACK, TX + * callbacks) */ + if (ver == 1 || ver == 2) { handle_tx_callback(drv, buf, data_len, ver == 2 ? 1 : 0); return; } else if (ver != 0) { @@ -266,7 +257,8 @@ static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr) } -static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack) +static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack, + unsigned int freq) { struct hostap_driver_data *drv = priv; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg; @@ -315,20 +307,21 @@ static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data, pos += 2; memcpy(pos, data, data_len); - res = hostap_send_mlme(drv, (u8 *) hdr, len, 0); + res = hostap_send_mlme(drv, (u8 *) hdr, len, 0, 0); if (res < 0) { wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - " "failed: %d (%s)", (unsigned long) len, errno, strerror(errno)); } - free(hdr); + os_free(hdr); return res; } static int hostap_sta_set_flags(void *priv, const u8 *addr, - int total_flags, int flags_or, int flags_and) + unsigned int total_flags, unsigned int flags_or, + unsigned int flags_and) { struct hostap_driver_data *drv = priv; struct prism2_hostapd_param param; @@ -470,18 +463,18 @@ static int hostap_get_seqnum(const char *ifname, void *priv, const u8 *addr, param = (struct prism2_hostapd_param *) buf; param->cmd = PRISM2_GET_ENCRYPTION; if (addr == NULL) - memset(param->sta_addr, 0xff, ETH_ALEN); + os_memset(param->sta_addr, 0xff, ETH_ALEN); else - memcpy(param->sta_addr, addr, ETH_ALEN); + os_memcpy(param->sta_addr, addr, ETH_ALEN); param->u.crypt.idx = idx; if (hostapd_ioctl(drv, param, blen)) { printf("Failed to get encryption.\n"); ret = -1; } else { - memcpy(seq, param->u.crypt.seq, 8); + os_memcpy(seq, param->u.crypt.seq, 8); } - free(buf); + os_free(buf); return ret; } @@ -1052,7 +1045,7 @@ static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, memcpy(mgmt.bssid, own_addr, ETH_ALEN); mgmt.u.deauth.reason_code = host_to_le16(reason); return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.deauth), 0); + sizeof(mgmt.u.deauth), 0, 0); } @@ -1090,7 +1083,7 @@ static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, memcpy(mgmt.bssid, own_addr, ETH_ALEN); mgmt.u.disassoc.reason_code = host_to_le16(reason); return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.disassoc), 0); + sizeof(mgmt.u.disassoc), 0, 0); } @@ -1168,7 +1161,7 @@ static void wpa_driver_hostap_poll_client(void *priv, const u8 *own_addr, os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); - hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0); + hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0, 0); } diff --git a/src/drivers/driver_hostap.h b/src/drivers/driver_hostap.h index a9d3e76cbe8f3..4c1e6d69f0a7c 100644 --- a/src/drivers/driver_hostap.h +++ b/src/drivers/driver_hostap.h @@ -192,7 +192,7 @@ struct prism2_hostapd_param { } mlme; struct { u8 ssid_len; - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; } scan_req; } u; }; diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c index 4953af6a16b29..669f1b813c43f 100644 --- a/src/drivers/driver_ndis.c +++ b/src/drivers/driver_ndis.c @@ -709,11 +709,11 @@ static int wpa_driver_ndis_radio_off(struct wpa_driver_ndis_data *drv) /* Disconnect by setting SSID to random (i.e., likely not used). */ static int wpa_driver_ndis_disconnect(struct wpa_driver_ndis_data *drv) { - char ssid[32]; + char ssid[SSID_MAX_LEN]; int i; - for (i = 0; i < 32; i++) + for (i = 0; i < SSID_MAX_LEN; i++) ssid[i] = rand() & 0xff; - return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, 32); + return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, SSID_MAX_LEN); } @@ -806,7 +806,7 @@ static struct wpa_scan_res * wpa_driver_ndis_add_scan_ssid( if (wpa_scan_get_ie(r, WLAN_EID_SSID)) return r; /* SSID IE already present */ - if (ssid->SsidLength == 0 || ssid->SsidLength > 32) + if (ssid->SsidLength == 0 || ssid->SsidLength > SSID_MAX_LEN) return r; /* No valid SSID inside scan data */ nr = os_realloc(r, sizeof(*r) + r->ie_len + 2 + ssid->SsidLength); diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index d7438683d8f01..00b173f3f85a3 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -87,7 +87,6 @@ static void nl80211_handle_destroy(struct nl_handle *handle) #undef nl_socket_set_nonblocking #define nl_socket_set_nonblocking(h) android_nl_socket_set_nonblocking(h) -#define genl_ctrl_resolve android_genl_ctrl_resolve #endif /* ANDROID */ @@ -1187,6 +1186,7 @@ static int get_link_signal(struct nl_msg *msg, void *arg) static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = { [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 }, + [NL80211_STA_INFO_BEACON_SIGNAL_AVG] = { .type = NLA_U8 }, }; struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { @@ -1215,6 +1215,13 @@ static int get_link_signal(struct nl_msg *msg, void *arg) else sig_change->avg_signal = 0; + if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]) + sig_change->avg_beacon_signal = + (s8) + nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]); + else + sig_change->avg_beacon_signal = 0; + if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, sinfo[NL80211_STA_INFO_TX_BITRATE], @@ -1871,6 +1878,11 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) ret = -1; } #endif /* CONFIG_TDLS */ +#ifdef CONFIG_FST + /* FST Action frames */ + if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0) + ret = -1; +#endif /* CONFIG_FST */ /* FT Action frames */ if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0) @@ -2493,7 +2505,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, { struct wpa_driver_nl80211_data *drv = bss->drv; int ifindex; - struct nl_msg *msg; + struct nl_msg *msg = NULL; int ret; int tdls = 0; @@ -2526,11 +2538,15 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, if (!msg) return -ENOBUFS; } else { + u32 suite; + + suite = wpa_alg_to_cipher_suite(alg, key_len); + if (!suite) + goto fail; msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_NEW_KEY); if (!msg || nla_put(msg, NL80211_ATTR_KEY_DATA, key_len, key) || - nla_put_u32(msg, NL80211_ATTR_KEY_CIPHER, - wpa_alg_to_cipher_suite(alg, key_len))) + nla_put_u32(msg, NL80211_ATTR_KEY_CIPHER, suite)) goto fail; wpa_hexdump_key(MSG_DEBUG, "nl80211: KEY_DATA", key, key_len); } @@ -2632,9 +2648,15 @@ static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg, const u8 *key, size_t key_len) { struct nlattr *key_attr = nla_nest_start(msg, NL80211_ATTR_KEY); + u32 suite; + if (!key_attr) return -1; + suite = wpa_alg_to_cipher_suite(alg, key_len); + if (!suite) + return -1; + if (defkey && alg == WPA_ALG_IGTK) { if (nla_put_flag(msg, NL80211_KEY_DEFAULT_MGMT)) return -1; @@ -2644,8 +2666,7 @@ static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg, } if (nla_put_u8(msg, NL80211_KEY_IDX, key_idx) || - nla_put_u32(msg, NL80211_KEY_CIPHER, - wpa_alg_to_cipher_suite(alg, key_len)) || + nla_put_u32(msg, NL80211_KEY_CIPHER, suite) || (seq && seq_len && nla_put(msg, NL80211_KEY_SEQ, seq_len, seq)) || nla_put(msg, NL80211_KEY_DATA, key_len, key)) @@ -3237,7 +3258,7 @@ static int wpa_driver_nl80211_set_acl(void *priv, struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - struct nlattr *acl; + struct nl_msg *acl; unsigned int i; int ret; @@ -3250,23 +3271,26 @@ static int wpa_driver_nl80211_set_acl(void *priv, wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)", params->acl_policy ? "Accept" : "Deny", params->num_mac_acl); + acl = nlmsg_alloc(); + if (!acl) + return -ENOMEM; + for (i = 0; i < params->num_mac_acl; i++) { + if (nla_put(acl, i + 1, ETH_ALEN, params->mac_acl[i].addr)) { + nlmsg_free(acl); + return -ENOMEM; + } + } + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MAC_ACL)) || nla_put_u32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ? NL80211_ACL_POLICY_DENY_UNLESS_LISTED : NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED) || - (acl = nla_nest_start(msg, NL80211_ATTR_MAC_ADDRS)) == NULL) { + nla_put_nested(msg, NL80211_ATTR_MAC_ADDRS, acl)) { nlmsg_free(msg); + nlmsg_free(acl); return -ENOMEM; } - - for (i = 0; i < params->num_mac_acl; i++) { - if (nla_put(msg, i + 1, ETH_ALEN, params->mac_acl[i].addr)) { - nlmsg_free(msg); - return -ENOMEM; - } - } - - nla_nest_end(msg, acl); + nlmsg_free(acl); ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) { @@ -4233,8 +4257,9 @@ static int wpa_driver_nl80211_hapd_send_eapol( static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, - int total_flags, - int flags_or, int flags_and) + unsigned int total_flags, + unsigned int flags_or, + unsigned int flags_and) { struct i802_bss *bss = priv; struct nl_msg *msg; @@ -5658,8 +5683,8 @@ static void *i802_init(struct hostapd_data *hapd, struct wpa_driver_nl80211_data *drv; struct i802_bss *bss; size_t i; - char brname[IFNAMSIZ]; - int ifindex, br_ifindex; + char master_ifname[IFNAMSIZ]; + int ifindex, br_ifindex = 0; int br_added = 0; bss = wpa_driver_nl80211_drv_init(hapd, params->ifname, @@ -5670,15 +5695,21 @@ static void *i802_init(struct hostapd_data *hapd, drv = bss->drv; - if (linux_br_get(brname, params->ifname) == 0) { + if (linux_br_get(master_ifname, params->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s", - params->ifname, brname); - br_ifindex = if_nametoindex(brname); - os_strlcpy(bss->brname, brname, IFNAMSIZ); + params->ifname, master_ifname); + br_ifindex = if_nametoindex(master_ifname); + os_strlcpy(bss->brname, master_ifname, IFNAMSIZ); + } else if ((params->num_bridge == 0 || !params->bridge[0]) && + linux_master_get(master_ifname, params->ifname) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in master %s", + params->ifname, master_ifname); + /* start listening for EAPOL on the master interface */ + add_ifidx(drv, if_nametoindex(master_ifname)); } else { - brname[0] = '\0'; - br_ifindex = 0; + master_ifname[0] = '\0'; } + bss->br_ifindex = br_ifindex; for (i = 0; i < params->num_bridge; i++) { @@ -5698,7 +5729,7 @@ static void *i802_init(struct hostapd_data *hapd, if (i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0) goto failed; - if (os_strcmp(params->bridge[0], brname) != 0) + if (os_strcmp(params->bridge[0], master_ifname) != 0) br_added = 1; } @@ -5776,13 +5807,12 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type( return NL80211_IFTYPE_P2P_DEVICE; case WPA_IF_MESH: return NL80211_IFTYPE_MESH_POINT; + default: + return -1; } - return -1; } -#if defined(CONFIG_P2P) || defined(CONFIG_MESH) - static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr) { struct wpa_driver_nl80211_data *drv; @@ -5818,8 +5848,6 @@ static int nl80211_vif_addr(struct wpa_driver_nl80211_data *drv, u8 *new_addr) return 0; } -#endif /* CONFIG_P2P || CONFIG_MESH */ - struct wdev_info { u64 wdev_id; @@ -5895,21 +5923,21 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } if (!addr) { - if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) + if (nlmode == NL80211_IFTYPE_P2P_DEVICE) os_memcpy(if_addr, bss->addr, ETH_ALEN); else if (linux_get_ifhwaddr(drv->global->ioctl_sock, - bss->ifname, if_addr) < 0) { + ifname, if_addr) < 0) { if (added) nl80211_remove_iface(drv, ifidx); return -1; } } -#if defined(CONFIG_P2P) || defined(CONFIG_MESH) if (!addr && (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP || - type == WPA_IF_P2P_GO || type == WPA_IF_MESH)) { - /* Enforce unique P2P Interface Address */ + type == WPA_IF_P2P_GO || type == WPA_IF_MESH || + type == WPA_IF_STATION)) { + /* Enforce unique address */ u8 new_addr[ETH_ALEN]; if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname, @@ -5920,8 +5948,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } if (nl80211_addr_in_use(drv->global, new_addr)) { wpa_printf(MSG_DEBUG, "nl80211: Allocate new address " - "for %s interface", type == WPA_IF_MESH ? - "mesh" : "P2P group"); + "for interface %s type %d", ifname, type); if (nl80211_vif_addr(drv, new_addr) < 0) { if (added) nl80211_remove_iface(drv, ifidx); @@ -5936,7 +5963,6 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } os_memcpy(if_addr, new_addr, ETH_ALEN); } -#endif /* CONFIG_P2P || CONFIG_MESH */ if (type == WPA_IF_AP_BSS) { struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss)); @@ -6514,47 +6540,6 @@ static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) } -static int wpa_driver_nl80211_shared_freq(void *priv) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct wpa_driver_nl80211_data *driver; - int freq = 0; - - /* - * If the same PHY is in connected state with some other interface, - * then retrieve the assoc freq. - */ - wpa_printf(MSG_DEBUG, "nl80211: Get shared freq for PHY %s", - drv->phyname); - - dl_list_for_each(driver, &drv->global->interfaces, - struct wpa_driver_nl80211_data, list) { - if (drv == driver || - os_strcmp(drv->phyname, driver->phyname) != 0 || - !driver->associated) - continue; - - wpa_printf(MSG_DEBUG, "nl80211: Found a match for PHY %s - %s " - MACSTR, - driver->phyname, driver->first_bss->ifname, - MAC2STR(driver->first_bss->addr)); - if (is_ap_interface(driver->nlmode)) - freq = driver->first_bss->freq; - else - freq = nl80211_get_assoc_freq(driver); - wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d", - drv->phyname, freq); - } - - if (!freq) - wpa_printf(MSG_DEBUG, "nl80211: No shared interface for " - "PHY (%s) in associated state", drv->phyname); - - return freq; -} - - static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, int encrypt) { @@ -7275,11 +7260,12 @@ static int driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type, static int driver_nl80211_send_mlme(void *priv, const u8 *data, - size_t data_len, int noack) + size_t data_len, int noack, + unsigned int freq) { struct i802_bss *bss = priv; return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack, - 0, 0, 0, 0); + freq, 0, 0, 0); } @@ -7506,7 +7492,10 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) "capa.max_acl_mac_addrs=%u\n" "capa.num_multichan_concurrent=%u\n" "capa.mac_addr_rand_sched_scan_supported=%d\n" - "capa.mac_addr_rand_scan_supported=%d\n", + "capa.mac_addr_rand_scan_supported=%d\n" + "capa.conc_capab=%u\n" + "capa.max_conc_chan_2_4=%u\n" + "capa.max_conc_chan_5_0=%u\n", drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth, @@ -7522,7 +7511,10 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) drv->capa.max_acl_mac_addrs, drv->capa.num_multichan_concurrent, drv->capa.mac_addr_rand_sched_scan_supported, - drv->capa.mac_addr_rand_scan_supported); + drv->capa.mac_addr_rand_scan_supported, + drv->capa.conc_capab, + drv->capa.max_conc_chan_2_4, + drv->capa.max_conc_chan_5_0); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; @@ -7837,7 +7829,7 @@ static int nl80211_set_wowlan(void *priv, wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan"); - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WOWLAN)) || + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_SET_WOWLAN)) || !(wowlan_triggers = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS)) || (triggers->any && @@ -8340,9 +8332,9 @@ static const char * drv_br_net_param_str(enum drv_br_net_param param) switch (param) { case DRV_BR_NET_PARAM_GARP_ACCEPT: return "arp_accept"; + default: + return NULL; } - - return NULL; } @@ -8354,6 +8346,13 @@ static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param, const char *param_txt; int ip_version = 4; + if (param == DRV_BR_MULTICAST_SNOOPING) { + os_snprintf(path, sizeof(path), + "/sys/devices/virtual/net/%s/bridge/multicast_snooping", + bss->brname); + goto set_val; + } + param_txt = drv_br_net_param_str(param); if (param_txt == NULL) return -EINVAL; @@ -8369,6 +8368,7 @@ static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param, os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s", ip_version, bss->brname, param_txt); +set_val: if (linux_write_system_file(path, val)) return -1; @@ -8387,12 +8387,34 @@ static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode) return QCA_ACS_MODE_IEEE80211A; case HOSTAPD_MODE_IEEE80211AD: return QCA_ACS_MODE_IEEE80211AD; + case HOSTAPD_MODE_IEEE80211ANY: + return QCA_ACS_MODE_IEEE80211ANY; default: return -1; } } +static int add_acs_freq_list(struct nl_msg *msg, const int *freq_list) +{ + int i, len, ret; + u32 *freqs; + + if (!freq_list) + return 0; + len = int_array_len(freq_list); + freqs = os_malloc(sizeof(u32) * len); + if (!freqs) + return -1; + for (i = 0; i < len; i++) + freqs[i] = freq_list[i]; + ret = nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST, + sizeof(u32) * len, freqs); + os_free(freqs); + return ret; +} + + static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params) { struct i802_bss *bss = priv; @@ -8415,12 +8437,25 @@ static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params) (params->ht_enabled && nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED)) || (params->ht40_enabled && - nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED))) { + nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED)) || + (params->vht_enabled && + nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED)) || + nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH, + params->ch_width) || + (params->ch_list_len && + nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST, params->ch_list_len, + params->ch_list)) || + add_acs_freq_list(msg, params->freq_list)) { nlmsg_free(msg); return -ENOBUFS; } nla_nest_end(msg, data); + wpa_printf(MSG_DEBUG, + "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d BW: %d CH_LIST_LEN: %u", + params->hw_mode, params->ht_enabled, params->ht40_enabled, + params->vht_enabled, params->ch_width, params->ch_list_len); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, @@ -8431,6 +8466,240 @@ static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params) } +static int nl80211_set_band(void *priv, enum set_band band) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *data; + int ret; + enum qca_set_band qca_band; + + if (!drv->setband_vendor_cmd_avail) + return -1; + + switch (band) { + case WPA_SETBAND_AUTO: + qca_band = QCA_SETBAND_AUTO; + break; + case WPA_SETBAND_5G: + qca_band = QCA_SETBAND_5G; + break; + case WPA_SETBAND_2G: + qca_band = QCA_SETBAND_2G; + break; + default: + 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_SETBAND) || + !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE, qca_band)) { + nlmsg_free(msg); + return -ENOBUFS; + } + nla_nest_end(msg, data); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, + "nl80211: Driver setband function failed: %s", + strerror(errno)); + } + return ret; +} + + +struct nl80211_pcl { + unsigned int num; + unsigned int *freq_list; +}; + +static int preferred_freq_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nl80211_pcl *param = arg; + struct nlattr *nl_vend, *attr; + enum qca_iface_type iface_type; + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1]; + unsigned int num, max_num; + u32 *freqs; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + nl_vend = tb[NL80211_ATTR_VENDOR_DATA]; + if (!nl_vend) + return NL_SKIP; + + nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX, + nla_data(nl_vend), nla_len(nl_vend), NULL); + + attr = tb_vendor[ + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE]; + if (!attr) { + wpa_printf(MSG_ERROR, "nl80211: iface_type couldn't be found"); + param->num = 0; + return NL_SKIP; + } + + iface_type = (enum qca_iface_type) nla_get_u32(attr); + wpa_printf(MSG_DEBUG, "nl80211: Driver returned iface_type=%d", + iface_type); + + attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST]; + if (!attr) { + wpa_printf(MSG_ERROR, + "nl80211: preferred_freq_list couldn't be found"); + param->num = 0; + return NL_SKIP; + } + + /* + * param->num has the maximum number of entries for which there + * is room in the freq_list provided by the caller. + */ + freqs = nla_data(attr); + max_num = nla_len(attr) / sizeof(u32); + if (max_num > param->num) + max_num = param->num; + for (num = 0; num < max_num; num++) + param->freq_list[num] = freqs[num]; + param->num = num; + + return NL_SKIP; +} + + +static int nl80211_get_pref_freq_list(void *priv, + enum wpa_driver_if_type if_type, + unsigned int *num, + unsigned int *freq_list) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + unsigned int i; + struct nlattr *params; + struct nl80211_pcl param; + enum qca_iface_type iface_type; + + if (!drv->get_pref_freq_list) + return -1; + + switch (if_type) { + case WPA_IF_STATION: + iface_type = QCA_IFACE_TYPE_STA; + break; + case WPA_IF_AP_BSS: + iface_type = QCA_IFACE_TYPE_AP; + break; + case WPA_IF_P2P_GO: + iface_type = QCA_IFACE_TYPE_P2P_GO; + break; + case WPA_IF_P2P_CLIENT: + iface_type = QCA_IFACE_TYPE_P2P_CLIENT; + break; + case WPA_IF_IBSS: + iface_type = QCA_IFACE_TYPE_IBSS; + break; + case WPA_IF_TDLS: + iface_type = QCA_IFACE_TYPE_TDLS; + break; + default: + return -1; + } + + param.num = *num; + param.freq_list = freq_list; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST) || + !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u32(msg, + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE, + iface_type)) { + wpa_printf(MSG_ERROR, + "%s: err in adding vendor_cmd and vendor_data", + __func__); + nlmsg_free(msg); + return -1; + } + nla_nest_end(msg, params); + + os_memset(freq_list, 0, *num * sizeof(freq_list[0])); + ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, ¶m); + if (ret) { + wpa_printf(MSG_ERROR, + "%s: err in send_and_recv_msgs", __func__); + return ret; + } + + *num = param.num; + + for (i = 0; i < *num; i++) { + wpa_printf(MSG_DEBUG, "nl80211: preferred_channel_list[%d]=%d", + i, freq_list[i]); + } + + return 0; +} + + +static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + struct nlattr *params; + + if (!drv->set_prob_oper_freq) + return -1; + + wpa_printf(MSG_DEBUG, + "nl80211: Set P2P probable operating freq %u for ifindex %d", + freq, bss->ifindex); + + 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_PROBABLE_OPER_CHANNEL) || + !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u32(msg, + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE, + QCA_IFACE_TYPE_P2P_CLIENT) || + nla_put_u32(msg, + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ, + freq)) { + wpa_printf(MSG_ERROR, + "%s: err in adding vendor_cmd and vendor_data", + __func__); + nlmsg_free(msg); + return -1; + } + nla_nest_end(msg, params); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_ERROR, "%s: err in send_and_recv_msgs", + __func__); + return ret; + } + nlmsg_free(msg); + return 0; +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -8490,7 +8759,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .signal_monitor = nl80211_signal_monitor, .signal_poll = nl80211_signal_poll, .send_frame = nl80211_send_frame, - .shared_freq = wpa_driver_nl80211_shared_freq, .set_param = nl80211_set_param, .get_radio_name = nl80211_get_radio_name, .add_pmkid = nl80211_add_pmkid, @@ -8518,7 +8786,9 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .set_ap_wps_ie = wpa_driver_set_ap_wps_p2p_ie, #endif /* ANDROID_P2P */ #ifdef ANDROID +#ifndef ANDROID_LIB_STUB .driver_cmd = wpa_driver_nl80211_driver_cmd, +#endif /* !ANDROID_LIB_STUB */ #endif /* ANDROID */ .vendor_cmd = nl80211_vendor_cmd, .set_qos_map = nl80211_set_qos_map, @@ -8537,4 +8807,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .add_tx_ts = nl80211_add_ts, .del_tx_ts = nl80211_del_ts, .do_acs = wpa_driver_do_acs, + .set_band = nl80211_set_band, + .get_pref_freq_list = nl80211_get_pref_freq_list, + .set_prob_oper_freq = nl80211_set_prob_oper_freq, }; diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index 802589aa75812..5c21e0faf55cd 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -110,7 +110,7 @@ struct wpa_driver_nl80211_data { u8 bssid[ETH_ALEN]; u8 prev_bssid[ETH_ALEN]; int associated; - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; enum nl80211_iftype nlmode; enum nl80211_iftype ap_scan_as_station; @@ -145,6 +145,9 @@ struct wpa_driver_nl80211_data { unsigned int get_features_vendor_cmd_avail:1; unsigned int set_rekey_offload:1; unsigned int p2p_go_ctwindow_supported:1; + unsigned int setband_vendor_cmd_avail:1; + unsigned int get_pref_freq_list:1; + unsigned int set_prob_oper_freq:1; u64 remain_on_chan_cookie; u64 send_action_cookie; @@ -169,7 +172,7 @@ struct wpa_driver_nl80211_data { /* From failed authentication command */ int auth_freq; u8 auth_bssid_[ETH_ALEN]; - u8 auth_ssid[32]; + u8 auth_ssid[SSID_MAX_LEN]; size_t auth_ssid_len; int auth_alg; u8 *auth_ie; @@ -232,7 +235,6 @@ int process_bss_event(struct nl_msg *msg, void *arg); #ifdef ANDROID int android_nl_socket_set_nonblocking(struct nl_handle *handle); -int android_genl_ctrl_resolve(struct nl_handle *handle, const char *name); int android_pno_start(struct i802_bss *bss, struct wpa_driver_scan_params *params); int android_pno_stop(struct i802_bss *bss); @@ -270,5 +272,6 @@ 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); +const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie); #endif /* DRIVER_NL80211_H */ diff --git a/src/drivers/driver_nl80211_android.c b/src/drivers/driver_nl80211_android.c index 3cc9a65867f64..ba47888843bbb 100644 --- a/src/drivers/driver_nl80211_android.c +++ b/src/drivers/driver_nl80211_android.c @@ -151,7 +151,7 @@ int android_pno_stop(struct i802_bss *bss) #ifdef ANDROID_P2P -#ifdef ANDROID_P2P_STUB +#ifdef ANDROID_LIB_STUB int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration) { @@ -178,7 +178,7 @@ int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon, return 0; } -#endif /* ANDROID_P2P_STUB */ +#endif /* ANDROID_LIB_STUB */ #endif /* ANDROID_P2P */ @@ -188,33 +188,3 @@ int android_nl_socket_set_nonblocking(struct nl_handle *handle) } -int android_genl_ctrl_resolve(struct nl_handle *handle, const char *name) -{ - /* - * Android ICS has very minimal genl_ctrl_resolve() implementation, so - * need to work around that. - */ - struct nl_cache *cache = NULL; - struct genl_family *nl80211 = NULL; - int id = -1; - - if (genl_ctrl_alloc_cache(handle, &cache) < 0) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " - "netlink cache"); - goto fail; - } - - nl80211 = genl_ctrl_search_by_name(cache, name); - if (nl80211 == NULL) - goto fail; - - id = genl_family_get_id(nl80211); - -fail: - if (nl80211) - genl_family_put(nl80211); - if (cache) - nl_cache_free(cache); - - return id; -} diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index e0d1d233e2ad5..4cf31238aeb70 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -335,6 +335,33 @@ static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls, } +static int ext_feature_isset(const u8 *ext_features, int ext_features_len, + enum nl80211_ext_feature_index ftidx) +{ + u8 ft_byte; + + if ((int) ftidx / 8 >= ext_features_len) + return 0; + + ft_byte = ext_features[ftidx / 8]; + return (ft_byte & BIT(ftidx % 8)) != 0; +} + + +static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, + struct nlattr *tb) +{ + struct wpa_driver_capa *capa = info->capa; + + if (tb == NULL) + return; + + if (ext_feature_isset(nla_data(tb), nla_len(tb), + NL80211_EXT_FEATURE_VHT_IBSS)) + capa->flags |= WPA_DRIVER_FLAGS_VHT_IBSS; +} + + static void wiphy_info_feature_flags(struct wiphy_info_data *info, struct nlattr *tb) { @@ -509,6 +536,7 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) info->device_ap_sme = 1; wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]); + wiphy_info_ext_feature_flags(info, tb[NL80211_ATTR_EXT_FEATURES]); wiphy_info_probe_resp_offload(capa, tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]); @@ -547,22 +575,34 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) continue; } vinfo = nla_data(nl); - switch (vinfo->subcmd) { - case QCA_NL80211_VENDOR_SUBCMD_TEST: - drv->vendor_cmd_test_avail = 1; - break; - case QCA_NL80211_VENDOR_SUBCMD_ROAMING: - drv->roaming_vendor_cmd_avail = 1; - break; - case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: - drv->dfs_vendor_cmd_avail = 1; - break; - case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: - drv->get_features_vendor_cmd_avail = 1; - break; - case QCA_NL80211_VENDOR_SUBCMD_DO_ACS: - drv->capa.flags |= WPA_DRIVER_FLAGS_ACS_OFFLOAD; - break; + if (vinfo->vendor_id == OUI_QCA) { + switch (vinfo->subcmd) { + case QCA_NL80211_VENDOR_SUBCMD_TEST: + drv->vendor_cmd_test_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_ROAMING: + drv->roaming_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: + drv->dfs_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: + drv->get_features_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST: + drv->get_pref_freq_list = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL: + drv->set_prob_oper_freq = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_DO_ACS: + drv->capa.flags |= + WPA_DRIVER_FLAGS_ACS_OFFLOAD; + break; + case QCA_NL80211_VENDOR_SUBCMD_SETBAND: + drv->setband_vendor_cmd_avail = 1; + break; + } } wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u", @@ -717,6 +757,7 @@ static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv) struct features_info { u8 *flags; size_t flags_len; + struct wpa_driver_capa *capa; }; @@ -742,6 +783,19 @@ static int features_info_handler(struct nl_msg *msg, void *arg) info->flags = nla_data(attr); info->flags_len = nla_len(attr); } + attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA]; + if (attr) + info->capa->conc_capab = nla_get_u32(attr); + + attr = tb_vendor[ + QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND]; + if (attr) + info->capa->max_conc_chan_2_4 = nla_get_u32(attr); + + attr = tb_vendor[ + QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND]; + if (attr) + info->capa->max_conc_chan_5_0 = nla_get_u32(attr); } return NL_SKIP; @@ -776,12 +830,16 @@ static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv) } os_memset(&info, 0, sizeof(info)); + info.capa = &drv->capa; ret = send_and_recv_msgs(drv, msg, features_info_handler, &info); if (ret || !info.flags) return; if (check_feature(QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD, &info)) drv->capa.flags |= WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD; + + if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info)) + drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY; } diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index 87e412dc596a5..7b0f721e65842 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -271,6 +271,7 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, struct nlattr *ptk_kek) { union wpa_event_data event; + const u8 *ssid; u16 status_code; if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { @@ -331,6 +332,16 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, if (req_ie) { event.assoc_info.req_ies = nla_data(req_ie); event.assoc_info.req_ies_len = nla_len(req_ie); + + if (cmd == NL80211_CMD_ROAM) { + ssid = nl80211_get_ie(event.assoc_info.req_ies, + event.assoc_info.req_ies_len, + WLAN_EID_SSID); + if (ssid && ssid[1] > 0 && ssid[1] <= 32) { + drv->ssid_len = ssid[1]; + os_memcpy(drv->ssid, ssid + 2, ssid[1]); + } + } } if (resp_ie) { event.assoc_info.resp_ies = nla_data(resp_ie); @@ -1480,6 +1491,25 @@ static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv, } +static enum hostapd_hw_mode get_qca_hw_mode(u8 hw_mode) +{ + switch (hw_mode) { + case QCA_ACS_MODE_IEEE80211B: + return HOSTAPD_MODE_IEEE80211B; + case QCA_ACS_MODE_IEEE80211G: + return HOSTAPD_MODE_IEEE80211G; + case QCA_ACS_MODE_IEEE80211A: + return HOSTAPD_MODE_IEEE80211A; + case QCA_ACS_MODE_IEEE80211AD: + return HOSTAPD_MODE_IEEE80211AD; + case QCA_ACS_MODE_IEEE80211ANY: + return HOSTAPD_MODE_IEEE80211ANY; + default: + return NUM_HOSTAPD_MODES; + } +} + + static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv, const u8 *data, size_t len) { @@ -1500,6 +1530,39 @@ static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv, nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]); event.acs_selected_channels.sec_channel = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]); + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]) + event.acs_selected_channels.vht_seg0_center_ch = + nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]); + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]) + event.acs_selected_channels.vht_seg1_center_ch = + nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]); + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]) + event.acs_selected_channels.ch_width = + nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]); + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]) { + u8 hw_mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]); + + event.acs_selected_channels.hw_mode = get_qca_hw_mode(hw_mode); + if (event.acs_selected_channels.hw_mode == NUM_HOSTAPD_MODES || + event.acs_selected_channels.hw_mode == + HOSTAPD_MODE_IEEE80211ANY) { + wpa_printf(MSG_DEBUG, + "nl80211: Invalid hw_mode %d in ACS selection event", + hw_mode); + return; + } + } + + wpa_printf(MSG_INFO, + "nl80211: ACS Results: PCH: %d SCH: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d", + event.acs_selected_channels.pri_channel, + event.acs_selected_channels.sec_channel, + event.acs_selected_channels.ch_width, + event.acs_selected_channels.vht_seg0_center_ch, + event.acs_selected_channels.vht_seg1_center_ch, + event.acs_selected_channels.hw_mode); + + /* Ignore ACS channel list check for backwards compatibility */ wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event); } diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c index 3911f485f72e4..4b762eafbe8ae 100644 --- a/src/drivers/driver_nl80211_scan.c +++ b/src/drivers/driver_nl80211_scan.c @@ -221,6 +221,9 @@ int wpa_driver_nl80211_scan(struct i802_bss *bss, wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request"); drv->scan_for_auth = 0; + if (TEST_FAIL()) + return -1; + msg = nl80211_scan_common(bss, NL80211_CMD_TRIGGER_SCAN, params); if (!msg) return -1; @@ -433,7 +436,7 @@ int wpa_driver_nl80211_stop_sched_scan(void *priv) } -static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie) +const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie) { const u8 *end, *pos; @@ -583,6 +586,11 @@ int bss_info_handler(struct nl_msg *msg, void *arg) r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID; if (bss[NL80211_BSS_TSF]) r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]); + if (bss[NL80211_BSS_BEACON_TSF]) { + u64 tsf = nla_get_u64(bss[NL80211_BSS_BEACON_TSF]); + if (tsf > r->tsf) + r->tsf = tsf; + } if (bss[NL80211_BSS_SEEN_MS_AGO]) r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]); r->ie_len = ie_len; diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c index de23fbd2b9fe1..1f1676a20ac58 100644 --- a/src/drivers/driver_privsep.c +++ b/src/drivers/driver_privsep.c @@ -220,6 +220,56 @@ static int wpa_driver_privsep_set_key(const char *ifname, void *priv, } +static int wpa_driver_privsep_authenticate( + void *priv, struct wpa_driver_auth_params *params) +{ + struct wpa_driver_privsep_data *drv = priv; + struct privsep_cmd_authenticate *data; + int i, res; + size_t buflen; + u8 *pos; + + wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d bssid=" MACSTR + " auth_alg=%d local_state_change=%d p2p=%d", + __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; + data = os_zalloc(buflen); + if (data == NULL) + return -1; + + data->freq = params->freq; + os_memcpy(data->bssid, params->bssid, ETH_ALEN); + os_memcpy(data->ssid, params->ssid, params->ssid_len); + data->ssid_len = params->ssid_len; + data->auth_alg = params->auth_alg; + data->ie_len = params->ie_len; + for (i = 0; i < 4; i++) { + if (params->wep_key[i]) + os_memcpy(data->wep_key[i], params->wep_key[i], + params->wep_key_len[i]); + data->wep_key_len[i] = params->wep_key_len[i]; + } + data->wep_tx_keyidx = params->wep_tx_keyidx; + data->local_state_change = params->local_state_change; + data->p2p = params->p2p; + pos = (u8 *) (data + 1); + if (params->ie_len) { + 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); + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_AUTHENTICATE, data, buflen, + NULL, NULL); + os_free(data); + + return res; +} + + static int wpa_driver_privsep_associate( void *priv, struct wpa_driver_associate_params *params) { @@ -281,14 +331,15 @@ static int wpa_driver_privsep_get_ssid(void *priv, u8 *ssid) { struct wpa_driver_privsep_data *drv = priv; int res, ssid_len; - u8 reply[sizeof(int) + 32]; + u8 reply[sizeof(int) + SSID_MAX_LEN]; size_t len = sizeof(reply); res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SSID, NULL, 0, reply, &len); if (res < 0 || len < sizeof(int)) return -1; os_memcpy(&ssid_len, reply, sizeof(int)); - if (ssid_len < 0 || ssid_len > 32 || sizeof(int) + ssid_len > len) { + if (ssid_len < 0 || ssid_len > SSID_MAX_LEN || + sizeof(int) + ssid_len > len) { wpa_printf(MSG_DEBUG, "privsep: Invalid get SSID reply"); return -1; } @@ -308,6 +359,32 @@ static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr, } +static void wpa_driver_privsep_event_auth(void *ctx, u8 *buf, size_t len) +{ + union wpa_event_data data; + struct privsep_event_auth *auth; + + os_memset(&data, 0, sizeof(data)); + if (len < sizeof(*auth)) + return; + auth = (struct privsep_event_auth *) buf; + if (len < sizeof(*auth) + auth->ies_len) + return; + + os_memcpy(data.auth.peer, auth->peer, ETH_ALEN); + os_memcpy(data.auth.bssid, auth->bssid, ETH_ALEN); + data.auth.auth_type = auth->auth_type; + data.auth.auth_transaction = auth->auth_transaction; + data.auth.status_code = auth->status_code; + if (auth->ies_len) { + data.auth.ies = (u8 *) (auth + 1); + data.auth.ies_len = auth->ies_len; + } + + wpa_supplicant_event(ctx, EVENT_AUTH, &data); +} + + static void wpa_driver_privsep_event_assoc(void *ctx, enum wpa_event_type event, u8 *buf, size_t len) @@ -467,6 +544,9 @@ static void wpa_driver_privsep_receive(int sock, void *eloop_ctx, case PRIVSEP_EVENT_SCAN_RESULTS: wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); break; + case PRIVSEP_EVENT_SCAN_STARTED: + wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL); + break; case PRIVSEP_EVENT_ASSOC: wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOC, event_buf, event_len); @@ -502,6 +582,9 @@ static void wpa_driver_privsep_receive(int sock, void *eloop_ctx, wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf, event_len); break; + case PRIVSEP_EVENT_AUTH: + wpa_driver_privsep_event_auth(drv->ctx, event_buf, event_len); + break; } os_free(buf); @@ -702,6 +785,10 @@ static int wpa_driver_privsep_get_capa(void *priv, res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_CAPA, NULL, 0, capa, &len); if (res < 0 || len != sizeof(*capa)) return -1; + /* For now, no support for passing extended_capa pointers */ + capa->extended_capa = NULL; + capa->extended_capa_mask = NULL; + capa->extended_capa_len = 0; return 0; } @@ -734,6 +821,7 @@ struct wpa_driver_ops wpa_driver_privsep_ops = { .set_param = wpa_driver_privsep_set_param, .scan2 = wpa_driver_privsep_scan, .deauthenticate = wpa_driver_privsep_deauthenticate, + .authenticate = wpa_driver_privsep_authenticate, .associate = wpa_driver_privsep_associate, .get_capa = wpa_driver_privsep_get_capa, .get_mac_addr = wpa_driver_privsep_get_mac_addr, @@ -742,7 +830,7 @@ struct wpa_driver_ops wpa_driver_privsep_ops = { }; -struct wpa_driver_ops *wpa_drivers[] = +const struct wpa_driver_ops *const wpa_drivers[] = { &wpa_driver_privsep_ops, NULL diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c index a1581b8c9cb1a..01defdff4f155 100644 --- a/src/drivers/driver_wext.c +++ b/src/drivers/driver_wext.c @@ -1,6 +1,6 @@ /* * Driver interaction with generic Linux Wireless Extensions - * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -18,6 +18,7 @@ #include <sys/stat.h> #include <fcntl.h> #include <net/if_arp.h> +#include <dirent.h> #include "linux_wext.h" #include "common.h" @@ -131,7 +132,7 @@ int wpa_driver_wext_get_ssid(void *priv, u8 *ssid) os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); iwr.u.essid.pointer = (caddr_t) ssid; - iwr.u.essid.length = 32; + iwr.u.essid.length = SSID_MAX_LEN; if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s", @@ -139,8 +140,8 @@ int wpa_driver_wext_get_ssid(void *priv, u8 *ssid) ret = -1; } else { ret = iwr.u.essid.length; - if (ret > 32) - ret = 32; + if (ret > SSID_MAX_LEN) + ret = SSID_MAX_LEN; /* Some drivers include nul termination in the SSID, so let's * remove it here before further processing. WE-21 changes this * to explicitly require the length _not_ to include nul @@ -168,7 +169,7 @@ int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len) int ret = 0; char buf[33]; - if (ssid_len > 32) + if (ssid_len > SSID_MAX_LEN) return -1; os_memset(&iwr, 0, sizeof(iwr)); @@ -874,6 +875,105 @@ static void wpa_driver_wext_send_rfkill(void *eloop_ctx, void *timeout_ctx) } +static int wext_hostap_ifname(struct wpa_driver_wext_data *drv, + const char *ifname) +{ + char buf[200], *res; + int type; + FILE *f; + + if (strcmp(ifname, ".") == 0 || strcmp(ifname, "..") == 0) + return -1; + + snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net/%s/type", + drv->ifname, ifname); + + f = fopen(buf, "r"); + if (!f) + return -1; + res = fgets(buf, sizeof(buf), f); + fclose(f); + + type = res ? atoi(res) : -1; + wpa_printf(MSG_DEBUG, "WEXT: hostap ifname %s type %d", ifname, type); + + if (type == ARPHRD_IEEE80211) { + wpa_printf(MSG_DEBUG, + "WEXT: Found hostap driver wifi# interface (%s)", + ifname); + wpa_driver_wext_alternative_ifindex(drv, ifname); + return 0; + } + return -1; +} + + +static int wext_add_hostap(struct wpa_driver_wext_data *drv) +{ + char buf[200]; + int n; + struct dirent **names; + int ret = -1; + + snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net", drv->ifname); + n = scandir(buf, &names, NULL, alphasort); + if (n < 0) + return -1; + + while (n--) { + if (ret < 0 && wext_hostap_ifname(drv, names[n]->d_name) == 0) + ret = 0; + free(names[n]); + } + free(names); + + return ret; +} + + +static void wext_check_hostap(struct wpa_driver_wext_data *drv) +{ + char buf[200], *pos; + ssize_t res; + + /* + * Host AP driver may use both wlan# and wifi# interface in wireless + * events. Since some of the versions included WE-18 support, let's add + * the alternative ifindex also from driver_wext.c for the time being. + * This may be removed at some point once it is believed that old + * versions of the driver are not in use anymore. However, it looks like + * the wifi# interface is still used in the current kernel tree, so it + * may not really be possible to remove this before the Host AP driver + * gets removed from the kernel. + */ + + /* First, try to see if driver information is available from sysfs */ + snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/driver", + drv->ifname); + res = readlink(buf, buf, sizeof(buf) - 1); + if (res > 0) { + buf[res] = '\0'; + pos = strrchr(buf, '/'); + if (pos) + pos++; + else + pos = buf; + wpa_printf(MSG_DEBUG, "WEXT: Driver: %s", pos); + if (os_strncmp(pos, "hostap", 6) == 0 && + wext_add_hostap(drv) == 0) + return; + } + + /* Second, use the old design with hardcoded ifname */ + if (os_strncmp(drv->ifname, "wlan", 4) == 0) { + char ifname2[IFNAMSIZ + 1]; + os_strlcpy(ifname2, drv->ifname, sizeof(ifname2)); + os_memcpy(ifname2, "wifi", 4); + wpa_driver_wext_alternative_ifindex(drv, ifname2); + } +} + + static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) { int send_rfkill_event = 0; @@ -914,20 +1014,7 @@ static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) drv->ifindex = if_nametoindex(drv->ifname); - if (os_strncmp(drv->ifname, "wlan", 4) == 0) { - /* - * Host AP driver may use both wlan# and wifi# interface in - * wireless events. Since some of the versions included WE-18 - * support, let's add the alternative ifindex also from - * driver_wext.c for the time being. This may be removed at - * some point once it is believed that old versions of the - * driver are not in use anymore. - */ - char ifname2[IFNAMSIZ + 1]; - os_strlcpy(ifname2, drv->ifname, sizeof(ifname2)); - os_memcpy(ifname2, "wifi", 4); - wpa_driver_wext_alternative_ifindex(drv, ifname2); - } + wext_check_hostap(drv); netlink_send_oper_ifla(drv->netlink, drv->ifindex, 1, IF_OPER_DORMANT); @@ -1112,7 +1199,7 @@ struct wext_scan_data { struct wpa_scan_res res; u8 *ie; size_t ie_len; - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; int maxrate; }; @@ -1865,7 +1952,7 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) { struct iwreq iwr; const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; int i; /* @@ -1907,9 +1994,9 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) * SIOCSIWMLME commands (or tries to associate automatically * after deauth/disassoc). */ - for (i = 0; i < 32; i++) + for (i = 0; i < SSID_MAX_LEN; i++) ssid[i] = rand() & 0xFF; - if (wpa_driver_wext_set_ssid(drv, ssid, 32) < 0) { + if (wpa_driver_wext_set_ssid(drv, ssid, SSID_MAX_LEN) < 0) { wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus " "SSID to disconnect"); } diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c index f0c3bb3c63ba0..a98af9ac7d711 100644 --- a/src/drivers/drivers.c +++ b/src/drivers/drivers.c @@ -47,7 +47,7 @@ extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */ #endif /* CONFIG_DRIVER_NONE */ -struct wpa_driver_ops *wpa_drivers[] = +const struct wpa_driver_ops *const wpa_drivers[] = { #ifdef CONFIG_DRIVER_NL80211 &wpa_driver_nl80211_ops, diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak index 9434078757018..3dd43c73803e7 100644 --- a/src/drivers/drivers.mak +++ b/src/drivers/drivers.mak @@ -54,7 +54,9 @@ else ifdef CONFIG_LIBNL_TINY DRV_LIBS += -lnl-tiny else - DRV_LIBS += -lnl + ifndef CONFIG_OSX + DRV_LIBS += -lnl + endif endif ifdef CONFIG_LIBNL20 diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c index 837971d25c99e..e21147af1bcd6 100644 --- a/src/drivers/linux_ioctl.c +++ b/src/drivers/linux_ioctl.c @@ -219,3 +219,26 @@ int linux_br_get(char *brname, const char *ifname) os_strlcpy(brname, pos, IFNAMSIZ); return 0; } + + +int linux_master_get(char *master_ifname, const char *ifname) +{ + char buf[128], masterlink[128], *pos; + ssize_t res; + + /* check whether there is a master */ + os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/master", ifname); + + res = readlink(buf, masterlink, sizeof(masterlink)); + if (res < 0 || (size_t) res >= sizeof(masterlink)) + return -1; + + masterlink[res] = '\0'; + + pos = os_strrchr(masterlink, '/'); + if (pos == NULL) + return -1; + pos++; + os_strlcpy(master_ifname, pos, IFNAMSIZ); + return 0; +} diff --git a/src/drivers/linux_ioctl.h b/src/drivers/linux_ioctl.h index c03fe6e9a3fd2..6de4d9b882751 100644 --- a/src/drivers/linux_ioctl.h +++ b/src/drivers/linux_ioctl.h @@ -18,5 +18,6 @@ int linux_br_del(int sock, const char *brname); int linux_br_add_if(int sock, const char *brname, const char *ifname); int linux_br_del_if(int sock, const char *brname, const char *ifname); int linux_br_get(char *brname, const char *ifname); +int linux_master_get(char *master_ifname, const char *ifname); #endif /* LINUX_IOCTL_H */ diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h index b37bd5a1cb825..ae16ba9cb1e31 100644 --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -25,10 +25,30 @@ * */ +/* + * This header file defines the userspace API to the wireless stack. Please + * be careful not to break things - i.e. don't move anything around or so + * unless you can demonstrate that it breaks neither API nor ABI. + * + * Additions to the API should be accompanied by actual implementations in + * an upstream driver, so that example implementations exist in case there + * are ever concerns about the precise semantics of the API or changes are + * needed, and to ensure that code for dead (no longer implemented) API + * can actually be identified and removed. + * Nonetheless, semantics should also be documented carefully in this file. + */ + #include <linux/types.h> #define NL80211_GENL_NAME "nl80211" +#define NL80211_MULTICAST_GROUP_CONFIG "config" +#define NL80211_MULTICAST_GROUP_SCAN "scan" +#define NL80211_MULTICAST_GROUP_REG "regulatory" +#define NL80211_MULTICAST_GROUP_MLME "mlme" +#define NL80211_MULTICAST_GROUP_VENDOR "vendor" +#define NL80211_MULTICAST_GROUP_TESTMODE "testmode" + /** * DOC: Station handling * @@ -173,8 +193,8 @@ * %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME. * * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration; - * either a dump request on a %NL80211_ATTR_WIPHY or a specific get - * on an %NL80211_ATTR_IFINDEX is supported. + * either a dump request for all interfaces or a specific get with a + * single %NL80211_ATTR_IFINDEX is supported. * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE. * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response @@ -252,7 +272,18 @@ * %NL80211_ATTR_IFINDEX. * * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set - * regulatory domain. + * regulatory domain. If %NL80211_ATTR_WIPHY is specified and the device + * has a private regulatory domain, it will be returned. Otherwise, the + * global regdomain will be returned. + * A device will have a private regulatory domain if it uses the + * regulatory_hint() API. Even when a private regdomain is used the channel + * information will still be mended according to further hints from + * the regulatory core to help with compliance. A dump version of this API + * is now available which will returns the global regdomain as well as + * all private regdomains of present wiphys (for those that have it). + * If a wiphy is self-managed (%NL80211_ATTR_WIPHY_SELF_MANAGED_REG), then + * its private regdomain is the only valid one for it. The regulatory + * core is not used to help with compliance in this case. * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command * after being queried by the kernel. CRDA replies by sending a regulatory * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our @@ -306,7 +337,9 @@ * if passed, define which channels should be scanned; if not * passed, all channels allowed for the current regulatory domain * are used. Extra IEs can also be passed from the userspace by - * using the %NL80211_ATTR_IE attribute. + * using the %NL80211_ATTR_IE attribute. The first cycle of the + * scheduled scan can be delayed by %NL80211_ATTR_SCHED_SCAN_DELAY + * is supplied. * @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. @@ -774,6 +807,10 @@ * peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel * when this command completes. * + * @NL80211_CMD_WIPHY_REG_CHANGE: Similar to %NL80211_CMD_REG_CHANGE, but used + * as an event to indicate changes for devices with wiphy-specific regdom + * management. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -958,6 +995,8 @@ enum nl80211_commands { NL80211_CMD_TDLS_CHANNEL_SWITCH, NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH, + NL80211_CMD_WIPHY_REG_CHANGE, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1655,6 +1694,13 @@ enum nl80211_commands { * @NL80211_ATTR_SOCKET_OWNER: Flag attribute, if set during interface * creation then the new interface will be owned by the netlink socket * that created it and will be destroyed when the socket is closed. + * If set during scheduled scan start then the new scan req will be + * owned by the netlink socket that created it and the scheduled scan will + * be stopped when the socket is closed. + * If set during configuration of regulatory indoor operation then the + * 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. * * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is * the TDLS link initiator. @@ -1688,6 +1734,32 @@ enum nl80211_commands { * * @NL80211_ATTR_MAC_MASK: MAC address mask * + * @NL80211_ATTR_WIPHY_SELF_MANAGED_REG: flag attribute indicating this device + * is self-managing its regulatory information and any regulatory domain + * obtained from it is coming from the device's wiphy and not the global + * cfg80211 regdomain. + * + * @NL80211_ATTR_EXT_FEATURES: extended feature flags contained in a byte + * array. The feature flags are identified by their bit index (see &enum + * nl80211_ext_feature_index). The bit index is ordered starting at the + * least-significant bit of the first byte in the array, ie. bit index 0 + * is located at bit 0 of byte 0. bit index 25 would be located at bit 1 + * of byte 3 (u8 array). + * + * @NL80211_ATTR_SURVEY_RADIO_STATS: Request overall radio statistics to be + * returned along with other survey data. If set, @NL80211_CMD_GET_SURVEY + * may return a survey entry without a channel indicating global radio + * statistics (only some values are valid and make sense.) + * For devices that don't return such an entry even then, the information + * should be contained in the result as the sum of the respective counters + * over all channels. + * + * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before a scheduled scan (or a + * WoWLAN net-detect scan) is started, u32 in seconds. + + * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device + * is operating in an indoor environment. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2045,6 +2117,18 @@ enum nl80211_attrs { NL80211_ATTR_MAC_MASK, + NL80211_ATTR_WIPHY_SELF_MANAGED_REG, + + NL80211_ATTR_EXT_FEATURES, + + NL80211_ATTR_SURVEY_RADIO_STATS, + + NL80211_ATTR_NETNS_FD, + + NL80211_ATTR_SCHED_SCAN_DELAY, + + NL80211_ATTR_REG_INDOOR, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2085,7 +2169,7 @@ enum nl80211_attrs { #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_HT_RATES 77 -#define NL80211_MAX_SUPP_REG_RULES 32 +#define NL80211_MAX_SUPP_REG_RULES 64 #define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 #define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 #define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 @@ -2225,8 +2309,15 @@ struct nl80211_sta_flag_update { * @NL80211_RATE_INFO_VHT_MCS: MCS index for VHT (u8) * @NL80211_RATE_INFO_VHT_NSS: number of streams in VHT (u8) * @NL80211_RATE_INFO_80_MHZ_WIDTH: 80 MHz VHT rate - * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: 80+80 MHz VHT rate + * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: unused - 80+80 is treated the + * same as 160 for purposes of the bitrates * @NL80211_RATE_INFO_160_MHZ_WIDTH: 160 MHz VHT rate + * @NL80211_RATE_INFO_10_MHZ_WIDTH: 10 MHz width - note that this is + * a legacy rate and will be reported as the actual bitrate, i.e. + * half the base (20 MHz) rate + * @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_AFTER_LAST: internal use */ enum nl80211_rate_info { @@ -2241,6 +2332,8 @@ enum nl80211_rate_info { NL80211_RATE_INFO_80_MHZ_WIDTH, NL80211_RATE_INFO_80P80_MHZ_WIDTH, NL80211_RATE_INFO_160_MHZ_WIDTH, + NL80211_RATE_INFO_10_MHZ_WIDTH, + NL80211_RATE_INFO_5_MHZ_WIDTH, /* keep last */ __NL80211_RATE_INFO_AFTER_LAST, @@ -2285,18 +2378,24 @@ enum nl80211_sta_bss_param { * * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs) - * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station) - * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station) - * @NL80211_STA_INFO_RX_BYTES64: total received bytes (u64, from this station) - * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (u64, to this station) + * @NL80211_STA_INFO_RX_BYTES: total received bytes (MPDU length) + * (u32, from this station) + * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (MPDU length) + * (u32, to this station) + * @NL80211_STA_INFO_RX_BYTES64: total received bytes (MPDU length) + * (u64, from this station) + * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (MPDU length) + * (u64, to this station) * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute * containing info as possible, see &enum nl80211_rate_info - * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station) - * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this - * station) - * @NL80211_STA_INFO_TX_RETRIES: total retries (u32, to this station) - * @NL80211_STA_INFO_TX_FAILED: total failed packets (u32, to this station) + * @NL80211_STA_INFO_RX_PACKETS: total received packet (MSDUs and MMPDUs) + * (u32, from this station) + * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (MSDUs and MMPDUs) + * (u32, to this station) + * @NL80211_STA_INFO_TX_RETRIES: total retries (MPDUs) (u32, to this station) + * @NL80211_STA_INFO_TX_FAILED: total failed packets (MPDUs) + * (u32, to this station) * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm) * @NL80211_STA_INFO_LLID: the station's mesh LLID * @NL80211_STA_INFO_PLID: the station's mesh PLID @@ -2320,6 +2419,16 @@ enum nl80211_sta_bss_param { * Same format as NL80211_STA_INFO_CHAIN_SIGNAL. * @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the * 802.11 header (u32, kbps) + * @NL80211_STA_INFO_RX_DROP_MISC: RX packets dropped for unspecified reasons + * (u64) + * @NL80211_STA_INFO_BEACON_RX: number of beacons received from this peer (u64) + * @NL80211_STA_INFO_BEACON_SIGNAL_AVG: signal strength average + * for beacons only (u8, dBm) + * @NL80211_STA_INFO_TID_STATS: per-TID statistics (see &enum nl80211_tid_stats) + * This is a nested attribute where each the inner attribute number is the + * TID+1 and the special TID 16 (i.e. value 17) is used for non-QoS frames; + * each one of those is again nested with &enum nl80211_tid_stats + * attributes carrying the actual values. * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -2352,6 +2461,10 @@ enum nl80211_sta_info { NL80211_STA_INFO_CHAIN_SIGNAL, NL80211_STA_INFO_CHAIN_SIGNAL_AVG, NL80211_STA_INFO_EXPECTED_THROUGHPUT, + NL80211_STA_INFO_RX_DROP_MISC, + NL80211_STA_INFO_BEACON_RX, + NL80211_STA_INFO_BEACON_SIGNAL_AVG, + NL80211_STA_INFO_TID_STATS, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, @@ -2359,6 +2472,31 @@ enum nl80211_sta_info { }; /** + * enum nl80211_tid_stats - per TID statistics attributes + * @__NL80211_TID_STATS_INVALID: attribute number 0 is reserved + * @NL80211_TID_STATS_RX_MSDU: number of MSDUs received (u64) + * @NL80211_TID_STATS_TX_MSDU: number of MSDUs transmitted (or + * attempted to transmit; u64) + * @NL80211_TID_STATS_TX_MSDU_RETRIES: number of retries for + * transmitted MSDUs (not counting the first attempt; u64) + * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted + * MSDUs (u64) + * @NUM_NL80211_TID_STATS: number of attributes here + * @NL80211_TID_STATS_MAX: highest numbered attribute here + */ +enum nl80211_tid_stats { + __NL80211_TID_STATS_INVALID, + NL80211_TID_STATS_RX_MSDU, + NL80211_TID_STATS_TX_MSDU, + NL80211_TID_STATS_TX_MSDU_RETRIES, + NL80211_TID_STATS_TX_MSDU_FAILED, + + /* keep last */ + NUM_NL80211_TID_STATS, + NL80211_TID_STATS_MAX = NUM_NL80211_TID_STATS - 1 +}; + +/** * enum nl80211_mpath_flags - nl80211 mesh path flags * * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active @@ -2772,16 +2910,18 @@ enum nl80211_user_reg_hint_type { * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm) * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used - * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio - * spent on this channel - * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary + * @NL80211_SURVEY_INFO_TIME: amount of time (in ms) that the radio + * was turned on (on channel or globally) + * @NL80211_SURVEY_INFO_TIME_BUSY: amount of the time the primary * channel was sensed busy (either due to activity or energy detect) - * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension + * @NL80211_SURVEY_INFO_TIME_EXT_BUSY: amount of time the extension * channel was sensed busy - * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent - * receiving data - * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent - * transmitting data + * @NL80211_SURVEY_INFO_TIME_RX: amount of time the radio spent + * receiving data (on channel or globally) + * @NL80211_SURVEY_INFO_TIME_TX: amount of time the radio spent + * transmitting data (on channel or globally) + * @NL80211_SURVEY_INFO_TIME_SCAN: time the radio spent for scan + * (on this channel or globally) * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number * currently defined * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use @@ -2791,17 +2931,25 @@ enum nl80211_survey_info { NL80211_SURVEY_INFO_FREQUENCY, NL80211_SURVEY_INFO_NOISE, NL80211_SURVEY_INFO_IN_USE, - NL80211_SURVEY_INFO_CHANNEL_TIME, - NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, - NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, - NL80211_SURVEY_INFO_CHANNEL_TIME_RX, - NL80211_SURVEY_INFO_CHANNEL_TIME_TX, + NL80211_SURVEY_INFO_TIME, + NL80211_SURVEY_INFO_TIME_BUSY, + NL80211_SURVEY_INFO_TIME_EXT_BUSY, + NL80211_SURVEY_INFO_TIME_RX, + NL80211_SURVEY_INFO_TIME_TX, + NL80211_SURVEY_INFO_TIME_SCAN, /* keep last */ __NL80211_SURVEY_INFO_AFTER_LAST, NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1 }; +/* keep old names for compatibility */ +#define NL80211_SURVEY_INFO_CHANNEL_TIME NL80211_SURVEY_INFO_TIME +#define NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY NL80211_SURVEY_INFO_TIME_BUSY +#define NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY NL80211_SURVEY_INFO_TIME_EXT_BUSY +#define NL80211_SURVEY_INFO_CHANNEL_TIME_RX NL80211_SURVEY_INFO_TIME_RX +#define NL80211_SURVEY_INFO_CHANNEL_TIME_TX NL80211_SURVEY_INFO_TIME_TX + /** * enum nl80211_mntr_flags - monitor configuration flags * @@ -2966,7 +3114,8 @@ enum nl80211_mesh_power_mode { * * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've * established peering with for longer than this time (in seconds), then - * remove it from the STA's list of peers. Default is 30 minutes. + * remove it from the STA's list of peers. You may set this to 0 to disable + * the removal of the STA. Default is 30 minutes. * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use */ @@ -3238,6 +3387,9 @@ enum nl80211_bss { /** * enum nl80211_bss_status - BSS "status" * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS. + * Note that this is no longer used since cfg80211 no longer + * keeps track of whether or not authentication was done with + * a given BSS. * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS. * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS. * @@ -3565,6 +3717,8 @@ struct nl80211_pattern_support { * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put * the chip into a special state -- works best with chips that have * support for low-power operation already (flag) + * Note that this mode is incompatible with all of the others, if + * any others are even supported by the device. * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect * is detected is implementation-specific (flag) * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed @@ -3621,9 +3775,12 @@ struct nl80211_pattern_support { * @NL80211_WOWLAN_TRIG_NET_DETECT: wake up when a configured network * is detected. This is a nested attribute that contains the * same attributes used with @NL80211_CMD_START_SCHED_SCAN. It - * specifies how the scan is performed (e.g. the interval and the - * channels to scan) as well as the scan results that will - * trigger a wake (i.e. the matchsets). + * specifies how the scan is performed (e.g. the interval, the + * channels to scan and the initial delay) as well as the scan + * results that will trigger a wake (i.e. the matchsets). This + * attribute is also sent in a response to + * @NL80211_CMD_GET_WIPHY, indicating the number of match sets + * supported by the driver (u32). * @NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS: nested attribute * containing an array with information about what triggered the * wake up. If no elements are present in the array, it means @@ -4194,6 +4351,21 @@ enum nl80211_feature_flags { }; /** + * enum nl80211_ext_feature_index - bit index of extended features. + * @NL80211_EXT_FEATURE_VHT_IBSS: This driver supports IBSS with VHT datarates. + * + * @NUM_NL80211_EXT_FEATURES: number of extended features. + * @MAX_NL80211_EXT_FEATURES: highest extended feature index. + */ +enum nl80211_ext_feature_index { + NL80211_EXT_FEATURE_VHT_IBSS, + + /* add new features before the definition below */ + NUM_NL80211_EXT_FEATURES, + MAX_NL80211_EXT_FEATURES = NUM_NL80211_EXT_FEATURES - 1 +}; + +/** * enum nl80211_probe_resp_offload_support_attr - optional supported * protocols for probe-response offloading by the driver/FW. * To be used with the %NL80211_ATTR_PROBE_RESP_OFFLOAD attribute. diff --git a/src/eap_common/Makefile b/src/eap_common/Makefile index adfd3dfd5b9be..f00b438c6188e 100644 --- a/src/eap_common/Makefile +++ b/src/eap_common/Makefile @@ -1,8 +1,31 @@ -all: - @echo Nothing to be made. +all: libeap_common.a clean: - rm -f *~ *.o *.d *.gcno *.gcda *.gcov + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeap_common.a install: @echo Nothing to be made. + +include ../lib.rules + +LIB_OBJS= \ + chap.o \ + eap_common.o \ + eap_eke_common.o \ + eap_eke_common.o \ + eap_fast_common.o \ + eap_gpsk_common.o \ + eap_ikev2_common.o \ + eap_pax_common.o \ + eap_peap_common.o \ + eap_psk_common.o \ + eap_pwd_common.o \ + eap_sake_common.o \ + eap_sim_common.o \ + eap_wsc_common.o \ + ikev2_common.o + +libeap_common.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/src/eap_common/eap_common.c b/src/eap_common/eap_common.c index 1de13281c5115..51a15d75bc938 100644 --- a/src/eap_common/eap_common.c +++ b/src/eap_common/eap_common.c @@ -192,7 +192,7 @@ u8 eap_get_id(const struct wpabuf *msg) /** - * eap_get_id - Get EAP Type from wpabuf + * eap_get_type - Get EAP Type from wpabuf * @msg: Buffer starting with an EAP header * Returns: The EAP Type after the EAP header */ diff --git a/src/eap_common/eap_fast_common.c b/src/eap_common/eap_fast_common.c index fceb1b0adc1c2..151cc7859c5da 100644 --- a/src/eap_common/eap_fast_common.c +++ b/src/eap_common/eap_fast_common.c @@ -96,49 +96,18 @@ void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random, u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, const char *label, size_t len) { - struct tls_keys keys; - u8 *rnd = NULL, *out; - int block_size; + u8 *out; - block_size = tls_connection_get_keyblock_size(ssl_ctx, conn); - if (block_size < 0) - return NULL; - - out = os_malloc(block_size + len); + out = os_malloc(len); if (out == NULL) return NULL; - if (tls_connection_prf(ssl_ctx, conn, label, 1, out, block_size + len) - == 0) { - os_memmove(out, out + block_size, len); - return out; + if (tls_connection_prf(ssl_ctx, conn, label, 1, 1, out, len)) { + os_free(out); + return NULL; } - if (tls_connection_get_keys(ssl_ctx, conn, &keys)) - goto fail; - - rnd = os_malloc(keys.client_random_len + keys.server_random_len); - if (rnd == NULL) - goto fail; - - os_memcpy(rnd, keys.server_random, keys.server_random_len); - os_memcpy(rnd + keys.server_random_len, keys.client_random, - keys.client_random_len); - - wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key " - "expansion", keys.master_key, keys.master_key_len); - if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, - label, rnd, keys.client_random_len + - keys.server_random_len, out, block_size + len)) - goto fail; - os_free(rnd); - os_memmove(out, out + block_size, len); return out; - -fail: - os_free(rnd); - os_free(out); - return NULL; } diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c index 631c363fb7c9f..4d27623f87bfd 100644 --- a/src/eap_common/eap_pwd_common.c +++ b/src/eap_common/eap_pwd_common.c @@ -86,9 +86,10 @@ static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label, * on the password and identities. */ int compute_password_element(EAP_PWD_group *grp, u16 num, - u8 *password, int password_len, - u8 *id_server, int id_server_len, - u8 *id_peer, int id_peer_len, u8 *token) + 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) { BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; struct crypto_hash *hash; @@ -283,10 +284,10 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, } -int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k, - BIGNUM *peer_scalar, BIGNUM *server_scalar, - u8 *confirm_peer, u8 *confirm_server, - u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id) +int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k, + const BIGNUM *peer_scalar, const 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; @@ -306,7 +307,7 @@ int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k, os_free(cruft); return -1; } - eap_pwd_h_update(hash, (u8 *) ciphersuite, sizeof(u32)); + 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); diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h index c54c4414f11f3..a0d717edfe7a8 100644 --- a/src/eap_common/eap_pwd_common.h +++ b/src/eap_common/eap_pwd_common.h @@ -56,10 +56,15 @@ struct eap_pwd_id { } STRUCT_PACKED; /* common routines */ -int compute_password_element(EAP_PWD_group *, u16, u8 *, int, u8 *, int, u8 *, - int, u8 *); -int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *, - u8 *, u8 *, u32 *, u8 *, u8 *, u8 *); +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, + 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); void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len); void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest); diff --git a/src/eap_common/eap_sake_common.c b/src/eap_common/eap_sake_common.c index a76253d00f606..c22e43ed84b60 100644 --- a/src/eap_common/eap_sake_common.c +++ b/src/eap_common/eap_sake_common.c @@ -16,99 +16,99 @@ static int eap_sake_parse_add_attr(struct eap_sake_parse_attr *attr, - const u8 *pos) + u8 attr_id, u8 len, const u8 *data) { size_t i; - switch (pos[0]) { + switch (attr_id) { case EAP_SAKE_AT_RAND_S: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_S"); - if (pos[1] != 2 + EAP_SAKE_RAND_LEN) { + if (len != EAP_SAKE_RAND_LEN) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_S with " - "invalid length %d", pos[1]); + "invalid payload length %d", len); return -1; } - attr->rand_s = pos + 2; + attr->rand_s = data; break; case EAP_SAKE_AT_RAND_P: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_P"); - if (pos[1] != 2 + EAP_SAKE_RAND_LEN) { + if (len != EAP_SAKE_RAND_LEN) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_P with " - "invalid length %d", pos[1]); + "invalid payload length %d", len); return -1; } - attr->rand_p = pos + 2; + attr->rand_p = data; break; case EAP_SAKE_AT_MIC_S: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_S"); - if (pos[1] != 2 + EAP_SAKE_MIC_LEN) { + if (len != EAP_SAKE_MIC_LEN) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_S with " - "invalid length %d", pos[1]); + "invalid payload length %d", len); return -1; } - attr->mic_s = pos + 2; + attr->mic_s = data; break; case EAP_SAKE_AT_MIC_P: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_P"); - if (pos[1] != 2 + EAP_SAKE_MIC_LEN) { + if (len != EAP_SAKE_MIC_LEN) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_P with " - "invalid length %d", pos[1]); + "invalid payload length %d", len); return -1; } - attr->mic_p = pos + 2; + attr->mic_p = data; break; case EAP_SAKE_AT_SERVERID: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SERVERID"); - attr->serverid = pos + 2; - attr->serverid_len = pos[1] - 2; + attr->serverid = data; + attr->serverid_len = len; break; case EAP_SAKE_AT_PEERID: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PEERID"); - attr->peerid = pos + 2; - attr->peerid_len = pos[1] - 2; + attr->peerid = data; + attr->peerid_len = len; break; case EAP_SAKE_AT_SPI_S: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_S"); - attr->spi_s = pos + 2; - attr->spi_s_len = pos[1] - 2; + attr->spi_s = data; + attr->spi_s_len = len; break; case EAP_SAKE_AT_SPI_P: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_P"); - attr->spi_p = pos + 2; - attr->spi_p_len = pos[1] - 2; + attr->spi_p = data; + attr->spi_p_len = len; break; case EAP_SAKE_AT_ANY_ID_REQ: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ANY_ID_REQ"); - if (pos[1] != 4) { + if (len != 2) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid AT_ANY_ID_REQ" - " length %d", pos[1]); + " payload length %d", len); return -1; } - attr->any_id_req = pos + 2; + attr->any_id_req = data; break; case EAP_SAKE_AT_PERM_ID_REQ: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PERM_ID_REQ"); - if (pos[1] != 4) { + if (len != 2) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid " - "AT_PERM_ID_REQ length %d", pos[1]); + "AT_PERM_ID_REQ payload length %d", len); return -1; } - attr->perm_id_req = pos + 2; + attr->perm_id_req = data; break; case EAP_SAKE_AT_ENCR_DATA: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ENCR_DATA"); - attr->encr_data = pos + 2; - attr->encr_data_len = pos[1] - 2; + attr->encr_data = data; + attr->encr_data_len = len; break; case EAP_SAKE_AT_IV: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV"); - attr->iv = pos + 2; - attr->iv_len = pos[1] - 2; + attr->iv = data; + attr->iv_len = len; break; case EAP_SAKE_AT_PADDING: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PADDING"); - for (i = 2; i < pos[1]; i++) { - if (pos[i]) { + for (i = 0; i < len; i++) { + if (data[i]) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_PADDING " "with non-zero pad byte"); return -1; @@ -117,26 +117,26 @@ static int eap_sake_parse_add_attr(struct eap_sake_parse_attr *attr, break; case EAP_SAKE_AT_NEXT_TMPID: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_NEXT_TMPID"); - attr->next_tmpid = pos + 2; - attr->next_tmpid_len = pos[1] - 2; + attr->next_tmpid = data; + attr->next_tmpid_len = len; break; case EAP_SAKE_AT_MSK_LIFE: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV"); - if (pos[1] != 6) { + if (len != 4) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid " - "AT_MSK_LIFE length %d", pos[1]); + "AT_MSK_LIFE payload length %d", len); return -1; } - attr->msk_life = pos + 2; + attr->msk_life = data; break; default: - if (pos[0] < 128) { + if (attr_id < 128) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown non-skippable" - " attribute %d", pos[0]); + " attribute %d", attr_id); return -1; } wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring unknown skippable " - "attribute %d", pos[0]); + "attribute %d", attr_id); break; } @@ -180,7 +180,7 @@ int eap_sake_parse_attributes(const u8 *buf, size_t len, return -1; } - if (eap_sake_parse_add_attr(attr, pos)) + if (eap_sake_parse_add_attr(attr, pos[0], pos[1] - 2, pos + 2)) return -1; pos += pos[1]; diff --git a/src/eap_common/ikev2_common.c b/src/eap_common/ikev2_common.c index 4f9e64eced08a..d60358c733f0b 100644 --- a/src/eap_common/ikev2_common.c +++ b/src/eap_common/ikev2_common.c @@ -16,7 +16,7 @@ #include "ikev2_common.h" -static struct ikev2_integ_alg ikev2_integ_algs[] = { +static const struct ikev2_integ_alg ikev2_integ_algs[] = { { AUTH_HMAC_SHA1_96, 20, 12 }, { AUTH_HMAC_MD5_96, 16, 12 } }; @@ -24,7 +24,7 @@ static struct ikev2_integ_alg ikev2_integ_algs[] = { #define NUM_INTEG_ALGS ARRAY_SIZE(ikev2_integ_algs) -static struct ikev2_prf_alg ikev2_prf_algs[] = { +static const struct ikev2_prf_alg ikev2_prf_algs[] = { { PRF_HMAC_SHA1, 20, 20 }, { PRF_HMAC_MD5, 16, 16 } }; @@ -32,7 +32,7 @@ static struct ikev2_prf_alg ikev2_prf_algs[] = { #define NUM_PRF_ALGS ARRAY_SIZE(ikev2_prf_algs) -static struct ikev2_encr_alg ikev2_encr_algs[] = { +static const struct ikev2_encr_alg ikev2_encr_algs[] = { { ENCR_AES_CBC, 16, 16 }, /* only 128-bit keys supported for now */ { ENCR_3DES, 24, 8 } }; diff --git a/src/eap_peer/Makefile b/src/eap_peer/Makefile index f79519b718499..6531ccd5dac0b 100644 --- a/src/eap_peer/Makefile +++ b/src/eap_peer/Makefile @@ -1,11 +1,23 @@ -all: - @echo Nothing to be made. +all: libeap_peer.a clean: - rm -f *~ *.o *.so *.d *.gcno *.gcda *.gcov + rm -f *~ *.o *.so *.d *.gcno *.gcda *.gcov libeap_peer.a install: if ls *.so >/dev/null 2>&1; then \ install -d $(DESTDIR)$(LIBDIR)/wpa_supplicant && \ cp *.so $(DESTDIR)$(LIBDIR)/wpa_supplicant \ ; fi + +include ../lib.rules + +CFLAGS += -DIEEE8021X_EAPOL + +LIB_OBJS= \ + eap.o \ + eap_methods.o + +libeap_peer.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index 35433f3bd8e41..56c24b5503200 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -584,7 +584,7 @@ static int eap_peer_erp_reauth_start(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP: Valid ERP key found %s (SEQ=%u)", erp->keyname_nai, erp->next_seq); - msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, + 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); if (msg == NULL) @@ -708,7 +708,7 @@ SM_STATE(EAP, SEND_RESPONSE) wpabuf_free(sm->lastRespData); if (sm->eapRespData) { if (sm->workaround) - os_memcpy(sm->last_md5, sm->req_md5, 16); + os_memcpy(sm->last_sha1, sm->req_sha1, 20); sm->lastId = sm->reqId; sm->lastRespData = wpabuf_dup(sm->eapRespData); eapol_set_bool(sm, EAPOL_eapResp, TRUE); @@ -914,12 +914,12 @@ static int eap_peer_req_is_duplicate(struct eap_sm *sm) duplicate = (sm->reqId == sm->lastId) && sm->rxReq; if (sm->workaround && duplicate && - os_memcmp(sm->req_md5, sm->last_md5, 16) != 0) { + os_memcmp(sm->req_sha1, sm->last_sha1, 20) != 0) { /* * RFC 4137 uses (reqId == lastId) as the only verification for * duplicate EAP requests. However, this misses cases where the * AS is incorrectly using the same id again; and - * unfortunately, such implementations exist. Use MD5 hash as + * unfortunately, such implementations exist. Use SHA1 hash as * an extra verification for the packets being duplicate to * workaround these issues. */ @@ -1765,7 +1765,7 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) if (sm->workaround) { const u8 *addr[1]; addr[0] = wpabuf_head(req); - md5_vector(1, addr, &plen, sm->req_md5); + sha1_vector(1, addr, &plen, sm->req_sha1); } switch (hdr->code) { @@ -1911,7 +1911,7 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, * structure remains alive while the EAP state machine is active. */ struct eap_sm * eap_peer_sm_init(void *eapol_ctx, - struct eapol_callbacks *eapol_cb, + const struct eapol_callbacks *eapol_cb, void *msg_ctx, struct eap_config *conf) { struct eap_sm *sm; @@ -2400,7 +2400,7 @@ static int eap_allowed_phase2_type(int vendor, int type) u32 eap_get_phase2_type(const char *name, int *vendor) { int v; - u8 type = eap_peer_get_type(name, &v); + u32 type = eap_peer_get_type(name, &v); if (eap_allowed_phase2_type(v, type)) { *vendor = v; return type; diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h index 702463b9d5146..1a645af8b2003 100644 --- a/src/eap_peer/eap.h +++ b/src/eap_peer/eap.h @@ -307,7 +307,7 @@ struct eap_config { }; struct eap_sm * eap_peer_sm_init(void *eapol_ctx, - struct eapol_callbacks *eapol_cb, + const struct eapol_callbacks *eapol_cb, void *msg_ctx, struct eap_config *conf); void eap_peer_sm_deinit(struct eap_sm *sm); int eap_peer_sm_step(struct eap_sm *sm); diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c index 0662ae7383674..dc9e8cc34d4af 100644 --- a/src/eap_peer/eap_aka.c +++ b/src/eap_peer/eap_aka.c @@ -1296,7 +1296,7 @@ static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv, pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, reqData, &len); - if (pos == NULL || len < 1) { + if (pos == NULL || len < 3) { ret->ignore = TRUE; return NULL; } diff --git a/src/eap_peer/eap_eke.c b/src/eap_peer/eap_eke.c index 9fec66c068679..dfbda5644f6fc 100644 --- a/src/eap_peer/eap_eke.c +++ b/src/eap_peer/eap_eke.c @@ -195,15 +195,14 @@ static int eap_eke_supp_mac(u8 mac) static struct wpabuf * eap_eke_build_fail(struct eap_eke_data *data, struct eap_method_ret *ret, - const struct wpabuf *reqData, - u32 failure_code) + u8 id, u32 failure_code) { struct wpabuf *resp; wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Failure/Response - code=0x%x", failure_code); - resp = eap_eke_build_msg(data, eap_get_id(reqData), 4, EAP_EKE_FAILURE); + resp = eap_eke_build_msg(data, id, 4, EAP_EKE_FAILURE); if (resp) wpabuf_put_be32(resp, failure_code); @@ -230,9 +229,10 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, const u8 *pos, *end; const u8 *prop = NULL; u8 idtype; + u8 id = eap_get_id(reqData); if (data->state != IDENTITY) { - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -240,7 +240,7 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, if (payload_len < 2 + 4) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -253,7 +253,7 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, if (pos + num_prop * 4 > end) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data (num_prop=%u)", num_prop); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -293,7 +293,7 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, if (prop == NULL) { wpa_printf(MSG_DEBUG, "EAP-EKE: No acceptable proposal found"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN); } @@ -301,7 +301,7 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, if (pos == end) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data to include IDType/Identity"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -312,7 +312,7 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, os_free(data->serverid); data->serverid = os_malloc(end - pos); if (data->serverid == NULL) { - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } os_memcpy(data->serverid, pos, end - pos); @@ -320,11 +320,11 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-ID/Response"); - resp = eap_eke_build_msg(data, eap_get_id(reqData), + resp = eap_eke_build_msg(data, id, 2 + 4 + 1 + data->peerid_len, EAP_EKE_ID); if (resp == NULL) { - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -339,7 +339,7 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, data->msgs = wpabuf_alloc(wpabuf_len(reqData) + wpabuf_len(resp)); if (data->msgs == NULL) { wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpabuf_put_buf(data->msgs, reqData); @@ -366,10 +366,11 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, u8 pub[EAP_EKE_MAX_DH_LEN]; const u8 *password; size_t password_len; + u8 id = eap_get_id(reqData); if (data->state != COMMIT) { wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Commit/Request received in unexpected state (%d)", data->state); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -378,7 +379,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, password = eap_get_config_password(sm, &password_len); if (password == NULL) { wpa_printf(MSG_INFO, "EAP-EKE: No password configured!"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PASSWD_NOT_FOUND); } @@ -387,7 +388,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (pos + data->sess.dhcomp_len > end) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -405,7 +406,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, data->serverid, data->serverid_len, data->peerid, data->peerid_len, key) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -416,7 +417,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH"); os_memset(key, 0, sizeof(key)); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -424,7 +425,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret"); os_memset(key, 0, sizeof(key)); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -433,18 +434,18 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, data->peerid, data->peerid_len) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki"); os_memset(key, 0, sizeof(key)); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Commit/Response"); - resp = eap_eke_build_msg(data, eap_get_id(reqData), + resp = eap_eke_build_msg(data, id, data->sess.dhcomp_len + data->sess.pnonce_len, EAP_EKE_COMMIT); if (resp == NULL) { os_memset(key, 0, sizeof(key)); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -453,7 +454,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_P"); os_memset(key, 0, sizeof(key)); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } os_memset(key, 0, sizeof(key)); @@ -463,7 +464,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (random_get_bytes(data->nonce_p, data->sess.nonce_len)) { wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P", @@ -472,7 +473,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (eap_eke_prot(&data->sess, data->nonce_p, data->sess.nonce_len, wpabuf_put(resp, 0), &prot_len) < 0) { wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", @@ -484,7 +485,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (wpabuf_resize(&data->msgs, wpabuf_len(reqData) + wpabuf_len(resp)) < 0) { wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpabuf_put_buf(data->msgs, reqData); @@ -509,11 +510,12 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, u8 auth_s[EAP_EKE_MAX_HASH_LEN]; size_t decrypt_len; u8 *auth; + u8 id = eap_get_id(reqData); if (data->state != CONFIRM) { wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Confirm/Request received in unexpected state (%d)", data->state); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -524,7 +526,7 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, if (pos + data->sess.pnonce_ps_len + data->sess.prf_len > end) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -532,19 +534,19 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, if (eap_eke_decrypt_prot(&data->sess, pos, data->sess.pnonce_ps_len, nonces, &decrypt_len) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_PS"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_AUTHENTICATION_FAIL); } if (decrypt_len != (size_t) 2 * data->sess.nonce_len) { wpa_printf(MSG_INFO, "EAP-EKE: PNonce_PS protected data length does not match length of Nonce_P and Nonce_S"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_AUTHENTICATION_FAIL); } wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_P | Nonce_S", nonces, 2 * data->sess.nonce_len); if (os_memcmp(data->nonce_p, nonces, data->sess.nonce_len) != 0) { wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_P does not match transmitted Nonce_P"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_AUTHENTICATION_FAIL); } @@ -556,30 +558,30 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, if (eap_eke_derive_ka(&data->sess, data->serverid, data->serverid_len, data->peerid, data->peerid_len, data->nonce_p, data->nonce_s) < 0) { - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth_s) < 0) { - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth_s, data->sess.prf_len); if (os_memcmp_const(auth_s, pos + data->sess.pnonce_ps_len, data->sess.prf_len) != 0) { wpa_printf(MSG_INFO, "EAP-EKE: Auth_S does not match"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_AUTHENTICATION_FAIL); } wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Confirm/Response"); - resp = eap_eke_build_msg(data, eap_get_id(reqData), + resp = eap_eke_build_msg(data, id, data->sess.pnonce_len + data->sess.prf_len, EAP_EKE_CONFIRM); if (resp == NULL) { - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -587,7 +589,7 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, if (eap_eke_prot(&data->sess, data->nonce_s, data->sess.nonce_len, wpabuf_put(resp, 0), &prot_len) < 0) { wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpabuf_put(resp, prot_len); @@ -595,7 +597,7 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, auth = wpabuf_put(resp, data->sess.prf_len); if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth) < 0) { wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth, data->sess.prf_len); @@ -606,7 +608,7 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, data->msk, data->emsk) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK"); wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -638,7 +640,8 @@ static struct wpabuf * eap_eke_process_failure(struct eap_eke_data *data, wpa_printf(MSG_INFO, "EAP-EKE: Failure-Code 0x%x", code); } - return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_NO_ERROR); + return eap_eke_build_fail(data, ret, eap_get_id(reqData), + EAP_EKE_FAIL_NO_ERROR); } @@ -741,6 +744,29 @@ static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_eke_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_data *data = priv; + u8 *sid; + size_t sid_len; + + if (data->state != SUCCESS) + return NULL; + + sid_len = 1 + 2 * data->sess.nonce_len; + sid = os_malloc(sid_len); + if (sid == NULL) + return NULL; + sid[0] = EAP_TYPE_EKE; + os_memcpy(sid + 1, data->nonce_p, data->sess.nonce_len); + os_memcpy(sid + 1 + data->sess.nonce_len, data->nonce_s, + data->sess.nonce_len); + *len = sid_len; + + return sid; +} + + int eap_peer_eke_register(void) { struct eap_method *eap; @@ -757,6 +783,7 @@ int eap_peer_eke_register(void) eap->isKeyAvailable = eap_eke_isKeyAvailable; eap->getKey = eap_eke_getKey; eap->get_emsk = eap_eke_get_emsk; + eap->getSessionId = eap_eke_get_session_id; ret = eap_peer_method_register(eap); if (ret) diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c index 68d7fba8892ef..4cbe3bacb0a61 100644 --- a/src/eap_peer/eap_fast.c +++ b/src/eap_peer/eap_fast.c @@ -267,8 +267,8 @@ static int eap_fast_derive_msk(struct eap_fast_data *data) } -static void eap_fast_derive_key_auth(struct eap_sm *sm, - struct eap_fast_data *data) +static int eap_fast_derive_key_auth(struct eap_sm *sm, + struct eap_fast_data *data) { u8 *sks; @@ -281,7 +281,7 @@ static void eap_fast_derive_key_auth(struct eap_sm *sm, if (sks == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive " "session_key_seed"); - return; + return -1; } /* @@ -294,11 +294,12 @@ static void eap_fast_derive_key_auth(struct eap_sm *sm, data->simck_idx = 0; os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN); os_free(sks); + return 0; } -static void eap_fast_derive_key_provisioning(struct eap_sm *sm, - struct eap_fast_data *data) +static int eap_fast_derive_key_provisioning(struct eap_sm *sm, + struct eap_fast_data *data) { os_free(data->key_block_p); data->key_block_p = (struct eap_fast_key_block_provisioning *) @@ -307,7 +308,7 @@ static void eap_fast_derive_key_provisioning(struct eap_sm *sm, sizeof(*data->key_block_p)); if (data->key_block_p == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block"); - return; + return -1; } /* * RFC 4851, Section 5.2: @@ -326,15 +327,19 @@ static void eap_fast_derive_key_provisioning(struct eap_sm *sm, wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge", data->key_block_p->client_challenge, sizeof(data->key_block_p->client_challenge)); + return 0; } -static void eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data) +static int eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data) { + int res; + if (data->anon_provisioning) - eap_fast_derive_key_provisioning(sm, data); + res = eap_fast_derive_key_provisioning(sm, data); else - eap_fast_derive_key_auth(sm, data); + res = eap_fast_derive_key_auth(sm, data); + return res; } @@ -1172,7 +1177,7 @@ static struct wpabuf * eap_fast_pac_request(void) static int eap_fast_process_decrypted(struct eap_sm *sm, struct eap_fast_data *data, struct eap_method_ret *ret, - const struct eap_hdr *req, + u8 identifier, struct wpabuf *decrypted, struct wpabuf **out_data) { @@ -1184,18 +1189,18 @@ static int eap_fast_process_decrypted(struct eap_sm *sm, return 0; if (resp) return eap_fast_encrypt_response(sm, data, resp, - req->identifier, out_data); + identifier, out_data); if (tlv.result == EAP_TLV_RESULT_FAILURE) { resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0); return eap_fast_encrypt_response(sm, data, resp, - req->identifier, out_data); + identifier, out_data); } if (tlv.iresult == EAP_TLV_RESULT_FAILURE) { resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1); return eap_fast_encrypt_response(sm, data, resp, - req->identifier, out_data); + identifier, out_data); } if (tlv.crypto_binding) { @@ -1277,14 +1282,13 @@ static int eap_fast_process_decrypted(struct eap_sm *sm, resp = wpabuf_alloc(1); } - return eap_fast_encrypt_response(sm, data, resp, req->identifier, + return eap_fast_encrypt_response(sm, data, resp, identifier, out_data); } static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, - struct eap_method_ret *ret, - const struct eap_hdr *req, + struct eap_method_ret *ret, u8 identifier, const struct wpabuf *in_data, struct wpabuf **out_data) { @@ -1309,7 +1313,7 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, /* Received TLS ACK - requesting more fragments */ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST, data->fast_version, - req->identifier, NULL, out_data); + identifier, NULL, out_data); } res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); @@ -1328,7 +1332,7 @@ continue_req: return -1; } - res = eap_fast_process_decrypted(sm, data, ret, req, + res = eap_fast_process_decrypted(sm, data, ret, identifier, in_decrypted, out_data); wpabuf_free(in_decrypted); @@ -1340,7 +1344,7 @@ continue_req: static const u8 * eap_fast_get_a_id(const u8 *buf, size_t len, size_t *id_len) { const u8 *a_id; - struct pac_tlv_hdr *hdr; + const struct pac_tlv_hdr *hdr; /* * Parse authority identity (A-ID) from the EAP-FAST/Start. This @@ -1350,13 +1354,13 @@ static const u8 * eap_fast_get_a_id(const u8 *buf, size_t len, size_t *id_len) *id_len = len; if (len > sizeof(*hdr)) { int tlen; - hdr = (struct pac_tlv_hdr *) buf; + hdr = (const struct pac_tlv_hdr *) buf; tlen = be_to_host16(hdr->len); if (be_to_host16(hdr->type) == PAC_TYPE_A_ID && sizeof(*hdr) + tlen <= len) { wpa_printf(MSG_DEBUG, "EAP-FAST: A-ID was in TLV " "(Start)"); - a_id = (u8 *) (hdr + 1); + a_id = (const u8 *) (hdr + 1); *id_len = tlen; } } @@ -1529,6 +1533,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, struct wpabuf *resp; const u8 *pos; struct eap_fast_data *data = priv; + struct wpabuf msg; pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_FAST, ret, reqData, &left, &flags); @@ -1545,13 +1550,13 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, left = 0; /* A-ID is not used in further packet processing */ } + wpabuf_set(&msg, pos, left); + resp = NULL; if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && !data->resuming) { /* Process tunneled (encrypted) phase 2 data. */ - struct wpabuf msg; - wpabuf_set(&msg, pos, left); - res = eap_fast_decrypt(sm, data, ret, req, &msg, &resp); + res = eap_fast_decrypt(sm, data, ret, id, &msg, &resp); if (res < 0) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; @@ -1565,8 +1570,15 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, /* Continue processing TLS handshake (phase 1). */ res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_FAST, - data->fast_version, id, pos, - left, &resp); + data->fast_version, id, &msg, + &resp); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "EAP-FAST: TLS processing failed"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return resp; + } if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { char cipher[80]; @@ -1586,20 +1598,24 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, } else data->anon_provisioning = 0; data->resuming = 0; - eap_fast_derive_keys(sm, data); + if (eap_fast_derive_keys(sm, data) < 0) { + wpa_printf(MSG_DEBUG, + "EAP-FAST: Could not derive keys"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + wpabuf_free(resp); + return NULL; + } } if (res == 2) { - struct wpabuf msg; /* * Application data included in the handshake message. */ wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = resp; resp = NULL; - wpabuf_set(&msg, pos, left); - res = eap_fast_decrypt(sm, data, ret, req, &msg, - &resp); + res = eap_fast_decrypt(sm, data, ret, id, &msg, &resp); } } diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c index c54bf116477c8..902b4ba26d6e4 100644 --- a/src/eap_peer/eap_gpsk.c +++ b/src/eap_peer/eap_gpsk.c @@ -274,7 +274,7 @@ static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm, static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm, struct eap_gpsk_data *data, struct eap_method_ret *ret, - const struct wpabuf *reqData, + u8 identifier, const u8 *payload, size_t payload_len) { @@ -301,7 +301,7 @@ static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm, return NULL; } - resp = eap_gpsk_send_gpsk_2(data, eap_get_id(reqData), + resp = eap_gpsk_send_gpsk_2(data, identifier, csuite_list, csuite_list_len); if (resp == NULL) return NULL; @@ -583,7 +583,7 @@ static const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data, static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm, struct eap_gpsk_data *data, struct eap_method_ret *ret, - const struct wpabuf *reqData, + u8 identifier, const u8 *payload, size_t payload_len) { @@ -615,7 +615,7 @@ static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm, (unsigned long) (end - pos)); } - resp = eap_gpsk_send_gpsk_4(data, eap_get_id(reqData)); + resp = eap_gpsk_send_gpsk_4(data, identifier); if (resp == NULL) return NULL; @@ -670,6 +670,7 @@ static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv, struct wpabuf *resp; const u8 *pos; size_t len; + u8 opcode, id; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len); if (pos == NULL || len < 1) { @@ -677,25 +678,27 @@ static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv, return NULL; } - wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", *pos); + id = eap_get_id(reqData); + opcode = *pos++; + len--; + wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", opcode); ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = FALSE; - switch (*pos) { + switch (opcode) { case EAP_GPSK_OPCODE_GPSK_1: - resp = eap_gpsk_process_gpsk_1(sm, data, ret, reqData, - pos + 1, len - 1); + resp = eap_gpsk_process_gpsk_1(sm, data, ret, id, pos, len); break; case EAP_GPSK_OPCODE_GPSK_3: - resp = eap_gpsk_process_gpsk_3(sm, data, ret, reqData, - pos + 1, len - 1); + resp = eap_gpsk_process_gpsk_3(sm, data, ret, id, pos, len); break; default: - wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignoring message with " - "unknown opcode %d", *pos); + wpa_printf(MSG_DEBUG, + "EAP-GPSK: Ignoring message with unknown opcode %d", + opcode); ret->ignore = TRUE; return NULL; } diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h index 2d7fdea227747..99b44dae4e34e 100644 --- a/src/eap_peer/eap_i.h +++ b/src/eap_peer/eap_i.h @@ -328,7 +328,7 @@ struct eap_sm { /* not defined in RFC 4137 */ Boolean changed; void *eapol_ctx; - struct eapol_callbacks *eapol_cb; + const struct eapol_callbacks *eapol_cb; void *eap_method_priv; int init_phase2; int fast_reauth; @@ -338,9 +338,9 @@ struct eap_sm { Boolean rxResp /* LEAP only */; Boolean leap_done; Boolean peap_done; - u8 req_md5[16]; /* MD5() of the current EAP packet */ - u8 last_md5[16]; /* MD5() of the previously received EAP packet; used - * in duplicate request detection. */ + u8 req_sha1[20]; /* SHA1() of the current EAP packet */ + u8 last_sha1[20]; /* SHA1() of the previously received EAP packet; used + * in duplicate request detection. */ void *msg_ctx; void *scard_ctx; diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c index 9e486e7d18fac..6acf1e8ad390a 100644 --- a/src/eap_peer/eap_mschapv2.c +++ b/src/eap_peer/eap_mschapv2.c @@ -511,6 +511,11 @@ static struct wpabuf * eap_mschapv2_change_password( struct eap_sm *sm, struct eap_mschapv2_data *data, struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id) { +#ifdef CONFIG_NO_RC4 + wpa_printf(MSG_ERROR, + "EAP-MSCHAPV2: RC4 not support in the build - cannot change password"); + return NULL; +#else /* CONFIG_NO_RC4 */ struct wpabuf *resp; int ms_len; const u8 *username, *password, *new_password; @@ -628,6 +633,7 @@ static struct wpabuf * eap_mschapv2_change_password( fail: wpabuf_free(resp); return NULL; +#endif /* CONFIG_NO_RC4 */ } diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c index 6d1ff208ac7ef..c920bcd3182f8 100644 --- a/src/eap_peer/eap_pax.c +++ b/src/eap_peer/eap_pax.c @@ -333,7 +333,7 @@ static struct wpabuf * eap_pax_process(struct eap_sm *sm, void *priv, u16 flen, mlen; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, reqData, &len); - if (pos == NULL || len < EAP_PAX_ICV_LEN) { + if (pos == NULL || len < sizeof(*req) + EAP_PAX_ICV_LEN) { ret->ignore = TRUE; return NULL; } diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c index 86a18bb866de4..98a48a6cf5d31 100644 --- a/src/eap_peer/eap_peap.c +++ b/src/eap_peer/eap_peap.c @@ -968,6 +968,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, struct wpabuf *resp; const u8 *pos; struct eap_peap_data *data = priv; + struct wpabuf msg; pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret, reqData, &left, &flags); @@ -998,18 +999,25 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, * should always be, anyway */ } + wpabuf_set(&msg, pos, left); + resp = NULL; if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && !data->resuming) { - struct wpabuf msg; - wpabuf_set(&msg, pos, left); res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp); } else { res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_PEAP, - data->peap_version, id, pos, - left, &resp); + data->peap_version, id, &msg, + &resp); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "EAP-PEAP: TLS processing failed"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return resp; + } if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { char *label; wpa_printf(MSG_DEBUG, @@ -1077,14 +1085,12 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, } if (res == 2) { - struct wpabuf msg; /* * Application data included in the handshake message. */ wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = resp; resp = NULL; - wpabuf_set(&msg, pos, left); res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp); } diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c index 059bbeecb72da..1f785443ee5ac 100644 --- a/src/eap_peer/eap_pwd.c +++ b/src/eap_peer/eap_pwd.c @@ -10,6 +10,7 @@ #include "common.h" #include "crypto/sha256.h" +#include "crypto/ms_funcs.h" #include "eap_peer/eap_i.h" #include "eap_common/eap_pwd_common.h" @@ -25,6 +26,7 @@ struct eap_pwd_data { size_t id_server_len; u8 *password; size_t password_len; + int password_hash; u16 group_num; EAP_PWD_group *grp; @@ -86,8 +88,9 @@ static void * eap_pwd_init(struct eap_sm *sm) const u8 *identity, *password; size_t identity_len, password_len; int fragment_size; + int pwhash; - password = eap_get_config_password(sm, &password_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); if (password == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: No password configured!"); return NULL; @@ -129,6 +132,7 @@ static void * eap_pwd_init(struct eap_sm *sm) } os_memcpy(data->password, password, password_len); data->password_len = password_len; + data->password_hash = pwhash; data->out_frag_pos = data->in_frag_pos = 0; data->inbuf = data->outbuf = NULL; @@ -216,6 +220,10 @@ 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; @@ -231,6 +239,9 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, id = (struct eap_pwd_id *) payload; data->group_num = be_to_host16(id->group_num); + wpa_printf(MSG_DEBUG, + "EAP-PWD: Server EAP-pwd-ID proposal: group=%u random=%u prf=%u prep=%u", + data->group_num, id->random_function, id->prf, id->prep); if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) || (id->prf != EAP_PWD_DEFAULT_PRF)) { ret->ignore = TRUE; @@ -238,6 +249,22 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, return; } + if (id->prep != EAP_PWD_PREP_NONE && + id->prep != EAP_PWD_PREP_MS) { + wpa_printf(MSG_DEBUG, + "EAP-PWD: Unsupported password pre-processing technique (Prep=%u)", + id->prep); + eap_pwd_state(data, FAILURE); + return; + } + + if (id->prep == EAP_PWD_PREP_NONE && data->password_hash) { + wpa_printf(MSG_DEBUG, + "EAP-PWD: Unhashed password not available"); + eap_pwd_state(data, FAILURE); + return; + } + wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d", data->group_num); @@ -260,12 +287,46 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, return; } + if (id->prep == EAP_PWD_PREP_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 (data->password_hash) { + res = hash_nt_password_hash(data->password, pwhashhash); + } else { + u8 pwhash[16]; + + res = nt_password_hash(data->password, + data->password_len, pwhash); + if (res == 0) + res = hash_nt_password_hash(pwhash, pwhashhash); + os_memset(pwhash, 0, sizeof(pwhash)); + } + + if (res) { + eap_pwd_state(data, FAILURE); + return; + } + + password = pwhashhash; + password_len = sizeof(pwhashhash); +#endif /* CONFIG_FIPS */ + } else { + password = data->password; + password_len = data->password_len; + } + /* compute PWE */ - if (compute_password_element(data->grp, data->group_num, - data->password, data->password_len, - data->id_server, data->id_server_len, - data->id_peer, data->id_peer_len, - id->token)) { + res = compute_password_element(data->grp, data->group_num, + password, password_len, + data->id_server, data->id_server_len, + data->id_peer, data->id_peer_len, + id->token); + os_memset(pwhashhash, 0, sizeof(pwhashhash)); + if (res) { wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE"); eap_pwd_state(data, FAILURE); return; @@ -301,6 +362,23 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, 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) || @@ -500,6 +578,18 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; int offset; + if (data->state != PWD_Confirm_Req) { + ret->ignore = TRUE; + goto fin; + } + + if (payload_len != SHA256_MAC_LEN) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Confirm payload length %u (expected %u)", + (unsigned int) payload_len, SHA256_MAC_LEN); + goto fin; + } + /* * first build up the ciphersuite which is group | random_function | * prf @@ -783,17 +873,30 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, * if it's the first fragment there'll be a length field */ if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { + if (len < 2) { + wpa_printf(MSG_DEBUG, + "EAP-pwd: Frame too short to contain Total-Length field"); + ret->ignore = TRUE; + return NULL; + } tot_len = WPA_GET_BE16(pos); wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose " "total length = %d", tot_len); if (tot_len > 15000) return NULL; + if (data->inbuf) { + wpa_printf(MSG_DEBUG, + "EAP-pwd: Unexpected new fragment start when previous fragment is still in use"); + ret->ignore = TRUE; + return NULL; + } data->inbuf = wpabuf_alloc(tot_len); if (data->inbuf == NULL) { wpa_printf(MSG_INFO, "Out of memory to buffer " "fragments!"); return NULL; } + data->in_frag_pos = 0; pos += sizeof(u16); len -= sizeof(u16); } @@ -873,6 +976,7 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, /* * we have output! Do we need to fragment it? */ + lm_exch = EAP_PWD_GET_EXCHANGE(lm_exch); len = wpabuf_len(data->outbuf); if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, data->mtu, diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c index 7d14907433e50..c4f9843febb35 100644 --- a/src/eap_peer/eap_sake.c +++ b/src/eap_peer/eap_sake.c @@ -141,7 +141,7 @@ static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data, static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm, struct eap_sake_data *data, struct eap_method_ret *ret, - const struct wpabuf *reqData, + u8 id, const u8 *payload, size_t payload_len) { @@ -166,8 +166,7 @@ static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Identity"); - resp = eap_sake_build_msg(data, eap_get_id(reqData), - 2 + data->peerid_len, + resp = eap_sake_build_msg(data, id, 2 + data->peerid_len, EAP_SAKE_SUBTYPE_IDENTITY); if (resp == NULL) return NULL; @@ -185,7 +184,7 @@ static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm, static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, struct eap_sake_data *data, struct eap_method_ret *ret, - const struct wpabuf *reqData, + u8 id, const u8 *payload, size_t payload_len) { @@ -247,8 +246,7 @@ static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, rlen = 2 + EAP_SAKE_RAND_LEN + 2 + EAP_SAKE_MIC_LEN; if (data->peerid) rlen += 2 + data->peerid_len; - resp = eap_sake_build_msg(data, eap_get_id(reqData), rlen, - EAP_SAKE_SUBTYPE_CHALLENGE); + resp = eap_sake_build_msg(data, id, rlen, EAP_SAKE_SUBTYPE_CHALLENGE); if (resp == NULL) return NULL; @@ -285,6 +283,7 @@ static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm, struct eap_sake_data *data, struct eap_method_ret *ret, + u8 id, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) @@ -323,14 +322,13 @@ static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm, ret->allowNotifications = FALSE; wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending " "Response/Auth-Reject"); - return eap_sake_build_msg(data, eap_get_id(reqData), 0, + return eap_sake_build_msg(data, id, 0, EAP_SAKE_SUBTYPE_AUTH_REJECT); } wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Confirm"); - resp = eap_sake_build_msg(data, eap_get_id(reqData), - 2 + EAP_SAKE_MIC_LEN, + resp = eap_sake_build_msg(data, id, 2 + EAP_SAKE_MIC_LEN, EAP_SAKE_SUBTYPE_CONFIRM); if (resp == NULL) return NULL; @@ -367,7 +365,7 @@ static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv, struct wpabuf *resp; const u8 *pos, *end; size_t len; - u8 subtype, session_id; + u8 subtype, session_id, id; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, reqData, &len); if (pos == NULL || len < sizeof(struct eap_sake_hdr)) { @@ -377,6 +375,7 @@ static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv, req = (const struct eap_sake_hdr *) pos; end = pos + len; + id = eap_get_id(reqData); subtype = req->subtype; session_id = req->session_id; pos = (const u8 *) (req + 1); @@ -402,15 +401,15 @@ static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv, switch (subtype) { case EAP_SAKE_SUBTYPE_IDENTITY: - resp = eap_sake_process_identity(sm, data, ret, reqData, + resp = eap_sake_process_identity(sm, data, ret, id, pos, end - pos); break; case EAP_SAKE_SUBTYPE_CHALLENGE: - resp = eap_sake_process_challenge(sm, data, ret, reqData, + resp = eap_sake_process_challenge(sm, data, ret, id, pos, end - pos); break; case EAP_SAKE_SUBTYPE_CONFIRM: - resp = eap_sake_process_confirm(sm, data, ret, reqData, + resp = eap_sake_process_confirm(sm, data, ret, id, reqData, pos, end - pos); break; default: diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c index bd06df78db4c4..99a2816ce61ed 100644 --- a/src/eap_peer/eap_sim.c +++ b/src/eap_peer/eap_sim.c @@ -1042,7 +1042,7 @@ static struct wpabuf * eap_sim_process(struct eap_sm *sm, void *priv, } pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, reqData, &len); - if (pos == NULL || len < 1) { + if (pos == NULL || len < 3) { ret->ignore = TRUE; return NULL; } diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c index 5aa3fd5912563..66a027a626e0d 100644 --- a/src/eap_peer/eap_tls.c +++ b/src/eap_peer/eap_tls.c @@ -156,20 +156,6 @@ static struct wpabuf * eap_tls_failure(struct eap_sm *sm, ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; - if (res == -1) { - struct eap_peer_config *config = eap_get_config(sm); - if (config) { - /* - * The TLS handshake failed. So better forget the old - * PIN. It may be wrong, we cannot be sure but trying - * the wrong one again might block it on the card--so - * better ask the user again. - */ - os_free(config->pin); - config->pin = NULL; - } - } - if (resp) { /* * This is likely an alert message, so send it instead of just @@ -228,6 +214,7 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, u8 flags, id; const u8 *pos; struct eap_tls_data *data = priv; + struct wpabuf msg; pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret, reqData, &left, &flags); @@ -242,8 +229,9 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, } resp = NULL; + wpabuf_set(&msg, pos, left); res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0, - id, pos, left, &resp); + id, &msg, &resp); if (res < 0) { return eap_tls_failure(sm, data, ret, res, resp, id); diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index 8710781618eff..af2b7541d7016 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -68,6 +68,10 @@ static void eap_tls_params_flags(struct tls_connection_params *params, params->flags |= TLS_CONN_DISABLE_SESSION_TICKET; if (os_strstr(txt, "tls_disable_session_ticket=0")) params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET; + if (os_strstr(txt, "tls_disable_tlsv1_0=1")) + params->flags |= TLS_CONN_DISABLE_TLSv1_0; + if (os_strstr(txt, "tls_disable_tlsv1_0=0")) + params->flags &= ~TLS_CONN_DISABLE_TLSv1_0; if (os_strstr(txt, "tls_disable_tlsv1_1=1")) params->flags |= TLS_CONN_DISABLE_TLSv1_1; if (os_strstr(txt, "tls_disable_tlsv1_1=0")) @@ -196,28 +200,25 @@ static int eap_tls_init_connection(struct eap_sm *sm, } res = tls_connection_set_params(data->ssl_ctx, data->conn, params); - if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) { + if (res == TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN) { /* - * At this point with the pkcs11 engine the PIN might be wrong. - * We reset the PIN in the configuration to be sure to not use - * it again and the calling function must request a new one. + * At this point with the pkcs11 engine the PIN is wrong. We + * reset the PIN in the configuration to be sure to not use it + * again and the calling function must request a new one. */ + wpa_printf(MSG_INFO, + "TLS: Bad PIN provided, requesting a new one"); os_free(config->pin); config->pin = NULL; + eap_sm_request_pin(sm); + sm->ignore = TRUE; + } else if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) { + wpa_printf(MSG_INFO, "TLS: Failed to initialize engine"); } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) { wpa_printf(MSG_INFO, "TLS: Failed to load private key"); - /* - * We do not know exactly but maybe the PIN was wrong, - * so ask for a new one. - */ - os_free(config->pin); - config->pin = NULL; - eap_sm_request_pin(sm); sm->ignore = TRUE; - tls_connection_deinit(data->ssl_ctx, data->conn); - data->conn = NULL; - return -1; - } else if (res) { + } + if (res) { wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection " "parameters"); tls_connection_deinit(data->ssl_ctx, data->conn); @@ -313,53 +314,19 @@ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, const char *label, size_t len) { -#ifndef CONFIG_FIPS - struct tls_keys keys; -#endif /* CONFIG_FIPS */ - u8 *rnd = NULL, *out; + u8 *out; out = os_malloc(len); if (out == NULL) return NULL; - /* First, try to use TLS library function for PRF, if available. */ - if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, out, len) - == 0) - return out; - -#ifndef CONFIG_FIPS - /* - * TLS library did not support key generation, so get the needed TLS - * session parameters and use an internal implementation of TLS PRF to - * derive the key. - */ - if (tls_connection_get_keys(data->ssl_ctx, data->conn, &keys)) - goto fail; - - if (keys.client_random == NULL || keys.server_random == NULL || - keys.master_key == NULL) - goto fail; - - rnd = os_malloc(keys.client_random_len + keys.server_random_len); - if (rnd == NULL) - goto fail; - os_memcpy(rnd, keys.client_random, keys.client_random_len); - os_memcpy(rnd + keys.client_random_len, keys.server_random, - keys.server_random_len); - - if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, - label, rnd, keys.client_random_len + - keys.server_random_len, out, len)) - goto fail; + if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, 0, + out, len)) { + os_free(out); + return NULL; + } - os_free(rnd); return out; - -fail: -#endif /* CONFIG_FIPS */ - os_free(out); - os_free(rnd); - return NULL; } @@ -380,10 +347,10 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, struct eap_ssl_data *data, u8 eap_type, size_t *len) { - struct tls_keys keys; + struct tls_random keys; u8 *out; - if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys)) return NULL; if (keys.client_random == NULL || keys.server_random == NULL) @@ -514,22 +481,19 @@ static const struct wpabuf * eap_peer_tls_data_reassemble( * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @in_data: Message received from the server - * @in_len: Length of in_data * @out_data: Buffer for returning a pointer to application data (if available) * Returns: 0 on success, 1 if more input data is needed, 2 if application data * is available, -1 on failure */ static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data, - const u8 *in_data, size_t in_len, + const struct wpabuf *in_data, struct wpabuf **out_data) { const struct wpabuf *msg; int need_more_input; struct wpabuf *appl_data; - struct wpabuf buf; - wpabuf_set(&buf, in_data, in_len); - msg = eap_peer_tls_data_reassemble(data, &buf, &need_more_input); + msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input); if (msg == NULL) return need_more_input ? 1 : -1; @@ -649,7 +613,6 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, * @peap_version: Version number for EAP-PEAP/TTLS * @id: EAP identifier for the response * @in_data: Message received from the server - * @in_len: Length of in_data * @out_data: Buffer for returning a pointer to the response message * Returns: 0 on success, 1 if more input data is needed, 2 if application data * is available, or -1 on failure @@ -672,14 +635,15 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, */ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, EapType eap_type, int peap_version, - u8 id, const u8 *in_data, size_t in_len, + u8 id, const struct wpabuf *in_data, struct wpabuf **out_data) { int ret = 0; *out_data = NULL; - if (data->tls_out && wpabuf_len(data->tls_out) > 0 && in_len > 0) { + if (data->tls_out && wpabuf_len(data->tls_out) > 0 && + wpabuf_len(in_data) > 0) { wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output " "fragments are waiting to be sent out"); return -1; @@ -690,8 +654,7 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, * No more data to send out - expect to receive more data from * the AS. */ - int res = eap_tls_process_input(sm, data, in_data, in_len, - out_data); + int res = eap_tls_process_input(sm, data, in_data, out_data); if (res) { /* * Input processing failed (res = -1) or more data is @@ -719,12 +682,18 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, if (tls_connection_get_failed(data->ssl_ctx, data->conn)) { /* TLS processing has failed - return error */ wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to " - "report error"); + "report error (len=%u)", + (unsigned int) wpabuf_len(data->tls_out)); ret = -1; /* TODO: clean pin if engine used? */ + if (wpabuf_len(data->tls_out) == 0) { + wpabuf_free(data->tls_out); + data->tls_out = NULL; + return -1; + } } - if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) { + if (wpabuf_len(data->tls_out) == 0) { /* * TLS negotiation should now be complete since all other cases * needing more data should have been caught above based on @@ -790,20 +759,24 @@ int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data) int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, char *buf, size_t buflen, int verbose) { - char name[128]; + char version[20], name[128]; int len = 0, ret; - if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) == 0) - { - ret = os_snprintf(buf + len, buflen - len, - "EAP TLS cipher=%s\n" - "tls_session_reused=%d\n", - name, tls_connection_resumed(data->ssl_ctx, - data->conn)); - if (os_snprintf_error(buflen - len, ret)) - return len; - len += ret; - } + if (tls_get_version(data->ssl_ctx, data->conn, version, + sizeof(version)) < 0) + version[0] = '\0'; + if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) < 0) + name[0] = '\0'; + + ret = os_snprintf(buf + len, buflen - len, + "eap_tls_version=%s\n" + "EAP TLS cipher=%s\n" + "tls_session_reused=%d\n", + version, name, + tls_connection_resumed(data->ssl_ctx, data->conn)); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; return len; } @@ -1032,7 +1005,7 @@ int eap_peer_select_phase2_methods(struct eap_peer_config *config, { char *start, *pos, *buf; struct eap_method_type *methods = NULL, *_methods; - u8 method; + u32 method; size_t num_methods = 0, prefix_len; if (config == NULL || config->phase2 == NULL) diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h index 390c2165927cb..acd2b783617fe 100644 --- a/src/eap_peer/eap_tls_common.h +++ b/src/eap_peer/eap_tls_common.h @@ -100,7 +100,7 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, size_t *len); int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, EapType eap_type, int peap_version, - u8 id, const u8 *in_data, size_t in_len, + u8 id, const struct wpabuf *in_data, struct wpabuf **out_data); struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type, int peap_version); diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c index b5c028b5276d5..b186c9156a741 100644 --- a/src/eap_peer/eap_ttls.c +++ b/src/eap_peer/eap_ttls.c @@ -175,7 +175,8 @@ static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, } avp->avp_code = host_to_be32(avp_code); - avp->avp_length = host_to_be32((flags << 24) | (u32) (hdrlen + len)); + avp->avp_length = host_to_be32(((u32) flags << 24) | + (u32) (hdrlen + len)); return avphdr + hdrlen; } @@ -253,11 +254,13 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm, } +#ifndef CONFIG_FIPS static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, struct eap_ttls_data *data, size_t len) { return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len); } +#endif /* CONFIG_FIPS */ static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data, @@ -428,6 +431,10 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, struct eap_method_ret *ret, struct wpabuf **resp) { +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPV2 not supported in FIPS build"); + return -1; +#else /* CONFIG_FIPS */ #ifdef EAP_MSCHAPv2 struct wpabuf *msg; u8 *buf, *pos, *challenge, *peer_challenge; @@ -510,6 +517,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); return -1; #endif /* EAP_MSCHAPv2 */ +#endif /* CONFIG_FIPS */ } @@ -518,6 +526,10 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, struct eap_method_ret *ret, struct wpabuf **resp) { +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAP not supported in FIPS build"); + return -1; +#else /* CONFIG_FIPS */ struct wpabuf *msg; u8 *buf, *pos, *challenge; const u8 *identity, *password; @@ -592,6 +604,7 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, ret->decision = DECISION_COND_SUCC; return 0; +#endif /* CONFIG_FIPS */ } @@ -654,6 +667,10 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm, struct eap_method_ret *ret, struct wpabuf **resp) { +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, "EAP-TTLS: CHAP not supported in FIPS build"); + return -1; +#else /* CONFIG_FIPS */ struct wpabuf *msg; u8 *buf, *pos, *challenge; const u8 *identity, *password; @@ -722,6 +739,7 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm, ret->decision = DECISION_COND_SUCC; return 0; +#endif /* CONFIG_FIPS */ } @@ -1385,14 +1403,20 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, u8 identifier, - const u8 *in_data, size_t in_len, + const struct wpabuf *in_data, struct wpabuf **out_data) { int res; res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS, data->ttls_version, identifier, - in_data, in_len, out_data); + in_data, out_data); + if (res < 0) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS processing failed"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to " @@ -1419,15 +1443,13 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, } if (res == 2) { - struct wpabuf msg; /* * Application data included in the handshake message. */ wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = *out_data; *out_data = NULL; - wpabuf_set(&msg, in_data, in_len); - res = eap_ttls_decrypt(sm, data, ret, identifier, &msg, + res = eap_ttls_decrypt(sm, data, ret, identifier, in_data, out_data); } @@ -1477,6 +1499,7 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, struct wpabuf *resp; const u8 *pos; struct eap_ttls_data *data = priv; + struct wpabuf msg; pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret, reqData, &left, &flags); @@ -1497,15 +1520,15 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, left = 0; } + wpabuf_set(&msg, pos, left); + resp = NULL; if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && !data->resuming) { - struct wpabuf msg; - wpabuf_set(&msg, pos, left); res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp); } else { res = eap_ttls_process_handshake(sm, data, ret, id, - pos, left, &resp); + &msg, &resp); } eap_ttls_check_auth_status(sm, data, ret); diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c index 7ce0a53d0b299..7ac99c7ce727b 100644 --- a/src/eap_peer/eap_wsc.c +++ b/src/eap_peer/eap_wsc.c @@ -557,6 +557,9 @@ send_msg: if (data->out_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive " "message from WPS"); + eap_wsc_state(data, FAIL); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; return NULL; } data->out_used = 0; diff --git a/src/eap_server/Makefile b/src/eap_server/Makefile index adfd3dfd5b9be..1172b72466d28 100644 --- a/src/eap_server/Makefile +++ b/src/eap_server/Makefile @@ -1,8 +1,21 @@ -all: - @echo Nothing to be made. +all: libeap_server.a clean: - rm -f *~ *.o *.d *.gcno *.gcda *.gcov + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeap_server.a install: @echo Nothing to be made. + +include ../lib.rules + +CFLAGS += -DCONFIG_HS20 + +LIB_OBJS= \ + eap_server.o \ + eap_server_identity.o \ + eap_server_methods.o + +libeap_server.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h index 9de6cb62f517b..69eaab8de946b 100644 --- a/src/eap_server/eap.h +++ b/src/eap_server/eap.h @@ -131,6 +131,7 @@ struct eap_config { const u8 *server_id; size_t server_id_len; int erp; + unsigned int tls_session_lifetime; #ifdef CONFIG_TESTING_OPTIONS u32 tls_test_flags; @@ -139,7 +140,7 @@ struct eap_config { struct eap_sm * eap_server_sm_init(void *eapol_ctx, - struct eapol_callbacks *eapol_cb, + const struct eapol_callbacks *eapol_cb, struct eap_config *eap_conf); void eap_server_sm_deinit(struct eap_sm *sm); int eap_server_sm_step(struct eap_sm *sm); @@ -149,5 +150,8 @@ int eap_sm_method_pending(struct eap_sm *sm); const u8 * eap_get_identity(struct eap_sm *sm, size_t *len); 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); #endif /* EAP_H */ diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h index 7d723091ffb5c..c90443d19cb93 100644 --- a/src/eap_server/eap_i.h +++ b/src/eap_server/eap_i.h @@ -155,7 +155,7 @@ struct eap_sm { /* not defined in RFC 4137 */ Boolean changed; void *eapol_ctx, *msg_ctx; - struct eapol_callbacks *eapol_cb; + const struct eapol_callbacks *eapol_cb; void *eap_method_priv; u8 *identity; size_t identity_len; @@ -210,6 +210,7 @@ struct eap_sm { Boolean initiate_reauth_start_sent; Boolean try_initiate_reauth; int erp; + unsigned int tls_session_lifetime; #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 bd919e570c825..84ecafc7ca3e5 100644 --- a/src/eap_server/eap_server.c +++ b/src/eap_server/eap_server.c @@ -96,7 +96,8 @@ static struct wpabuf * eap_sm_buildInitiateReauthStart(struct eap_sm *sm, plen += 2 + domain_len; } - msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH_START, plen, + msg = eap_msg_alloc(EAP_VENDOR_IETF, + (EapType) EAP_ERP_TYPE_REAUTH_START, plen, EAP_CODE_INITIATE, id); if (msg == NULL) return NULL; @@ -714,8 +715,8 @@ static void erp_send_finish_reauth(struct eap_sm *sm, plen = 1 + 2 + 2 + os_strlen(nai); if (hash_len) plen += 1 + hash_len; - msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, plen, - EAP_CODE_FINISH, id); + msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH, + plen, EAP_CODE_FINISH, id); if (msg == NULL) return; wpabuf_put_u8(msg, flags); @@ -745,7 +746,7 @@ static void erp_send_finish_reauth(struct eap_sm *sm, wpabuf_free(sm->lastReqData); sm->lastReqData = NULL; - if (flags & 0x80) { + if ((flags & 0x80) || !erp) { sm->eap_if.eapFail = TRUE; wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE MACSTR, MAC2STR(sm->peer_addr)); @@ -799,7 +800,7 @@ SM_STATE(EAP, INITIATE_RECEIVED) sm->rxInitiate = FALSE; - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, + pos = eap_hdr_validate(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH, sm->eap_if.eapRespData, &len); if (pos == NULL) { wpa_printf(MSG_INFO, "EAP-Initiate: Invalid frame"); @@ -1246,6 +1247,17 @@ SM_STEP(EAP) break; } SM_ENTER(EAP, SEND_REQUEST); + if (sm->eap_if.eapNoReq && !sm->eap_if.eapReq) { + /* + * This transition is not mentioned in RFC 4137, but it + * is needed to handle cleanly a case where EAP method + * buildReq fails. + */ + wpa_printf(MSG_DEBUG, + "EAP: Method did not return a request"); + SM_ENTER(EAP, FAILURE); + break; + } break; case EAP_METHOD_RESPONSE: /* @@ -1802,7 +1814,7 @@ static void eap_user_free(struct eap_user *user) * This function allocates and initializes an EAP state machine. */ struct eap_sm * eap_server_sm_init(void *eapol_ctx, - struct eapol_callbacks *eapol_cb, + const struct eapol_callbacks *eapol_cb, struct eap_config *conf) { struct eap_sm *sm; @@ -1853,6 +1865,7 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx, sm->server_id = conf->server_id; sm->server_id_len = conf->server_id_len; sm->erp = conf->erp; + sm->tls_session_lifetime = conf->tls_session_lifetime; #ifdef CONFIG_TESTING_OPTIONS sm->tls_test_flags = conf->tls_test_flags; @@ -1979,3 +1992,25 @@ void eap_server_clear_identity(struct eap_sm *sm) os_free(sm->identity); sm->identity = NULL; } + + +#ifdef CONFIG_TESTING_OPTIONS +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) +{ + char hex_challenge[30], hex_response[90], user[100]; + + /* Print out Challenge and Response in format supported by asleap. */ + if (username) + printf_encode(user, sizeof(user), username, username_len); + else + user[0] = '\0'; + wpa_snprintf_hex_sep(hex_challenge, sizeof(hex_challenge), + challenge, sizeof(challenge), ':'); + wpa_snprintf_hex_sep(hex_response, sizeof(hex_response), response, 24, + ':'); + wpa_printf(MSG_DEBUG, "[%s/user=%s] asleap -C %s -R %s", + source, user, hex_challenge, hex_response); +} +#endif /* CONFIG_TESTING_OPTIONS */ diff --git a/src/eap_server/eap_server_eke.c b/src/eap_server/eap_server_eke.c index 966f511ddddca..ba82be9c3f3ad 100644 --- a/src/eap_server/eap_server_eke.c +++ b/src/eap_server/eap_server_eke.c @@ -766,6 +766,29 @@ static Boolean eap_eke_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_eke_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_data *data = priv; + u8 *sid; + size_t sid_len; + + if (data->state != SUCCESS) + return NULL; + + sid_len = 1 + 2 * data->sess.nonce_len; + sid = os_malloc(sid_len); + if (sid == NULL) + return NULL; + sid[0] = EAP_TYPE_EKE; + os_memcpy(sid + 1, data->nonce_p, data->sess.nonce_len); + os_memcpy(sid + 1 + data->sess.nonce_len, data->nonce_s, + data->sess.nonce_len); + *len = sid_len; + + return sid; +} + + int eap_server_eke_register(void) { struct eap_method *eap; @@ -785,6 +808,7 @@ int eap_server_eke_register(void) eap->getKey = eap_eke_getKey; eap->isSuccess = eap_eke_isSuccess; eap->get_emsk = eap_eke_get_emsk; + eap->getSessionId = eap_eke_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c index 6745100d338c0..bd9018e78b56b 100644 --- a/src/eap_server/eap_server_fast.c +++ b/src/eap_server/eap_server_fast.c @@ -428,7 +428,7 @@ static void * eap_fast_init(struct eap_sm *sm) } data->state = START; - if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_FAST)) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL."); eap_fast_reset(sm, data); return NULL; diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c index 05848d2eaac55..98d74e0d717e2 100644 --- a/src/eap_server/eap_server_mschapv2.c +++ b/src/eap_server/eap_server_mschapv2.c @@ -360,6 +360,19 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, } } +#ifdef CONFIG_TESTING_OPTIONS + { + u8 challenge[8]; + + if (challenge_hash(peer_challenge, data->auth_challenge, + username, username_len, challenge) == 0) { + eap_server_mschap_rx_callback(sm, "EAP-MSCHAPV2", + username, username_len, + challenge, nt_response); + } + } +#endif /* CONFIG_TESTING_OPTIONS */ + if (username_len != user_len || os_memcmp(username, user, username_len) != 0) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names"); diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c index faa0fd2f23878..51062b0987e45 100644 --- a/src/eap_server/eap_server_peap.c +++ b/src/eap_server/eap_server_peap.c @@ -95,6 +95,37 @@ static void eap_peap_state(struct eap_peap_data *data, int state) eap_peap_state_txt(data->state), eap_peap_state_txt(state)); data->state = state; + if (state == FAILURE || state == FAILURE_REQ) + tls_connection_remove_session(data->ssl.conn); +} + + +static void eap_peap_valid_session(struct eap_sm *sm, + struct eap_peap_data *data) +{ + struct wpabuf *buf; + + if (!sm->tls_session_lifetime || + tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) + return; + + buf = wpabuf_alloc(1 + 1 + sm->identity_len); + if (!buf) + return; + wpabuf_put_u8(buf, EAP_TYPE_PEAP); + if (sm->identity) { + u8 id_len; + + if (sm->identity_len <= 255) + id_len = sm->identity_len; + else + id_len = 255; + wpabuf_put_u8(buf, id_len); + wpabuf_put_data(buf, sm->identity, id_len); + } else { + wpabuf_put_u8(buf, 0); + } + tls_connection_set_success_data(data->ssl.conn, buf); } @@ -151,7 +182,7 @@ static void * eap_peap_init(struct eap_sm *sm) data->state = START; data->crypto_binding = OPTIONAL_BINDING; - if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_PEAP)) { wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); eap_peap_reset(sm, data); return NULL; @@ -539,15 +570,14 @@ static Boolean eap_peap_check(struct eap_sm *sm, void *priv, static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data, - EapType eap_type) + int vendor, EapType eap_type) { if (data->phase2_priv && data->phase2_method) { data->phase2_method->reset(sm, data->phase2_priv); data->phase2_method = NULL; data->phase2_priv = NULL; } - data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF, - eap_type); + data->phase2_method = eap_server_get_eap_method(vendor, eap_type); if (!data->phase2_method) return -1; @@ -709,10 +739,12 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm, if (status == EAP_TLV_RESULT_SUCCESS) { wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Success " "- requested %s", requested); - if (data->tlv_request == TLV_REQ_SUCCESS) + if (data->tlv_request == TLV_REQ_SUCCESS) { eap_peap_state(data, SUCCESS); - else + eap_peap_valid_session(sm, data); + } else { eap_peap_state(data, FAILURE); + } } else if (status == EAP_TLV_RESULT_FAILURE) { wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Failure " @@ -737,7 +769,7 @@ static void eap_peap_process_phase2_soh(struct eap_sm *sm, const u8 *soh_tlv = NULL; size_t soh_tlv_len = 0; int tlv_type, mandatory, tlv_len, vtlv_len; - u8 next_type; + u32 next_type; u32 vendor_id; pos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21, in_data, &left); @@ -852,8 +884,9 @@ auth_method: eap_peap_state(data, PHASE2_METHOD); next_type = sm->user->methods[0].method; sm->user_eap_method_index = 1; - wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); - eap_peap_phase2_init(sm, data, next_type); + wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP vendor %d type %d", + sm->user->methods[0].vendor, next_type); + eap_peap_phase2_init(sm, data, sm->user->methods[0].vendor, next_type); } #endif /* EAP_SERVER_TNC */ @@ -862,7 +895,8 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, struct eap_peap_data *data, struct wpabuf *in_data) { - u8 next_type = EAP_TYPE_NONE; + int next_vendor = EAP_VENDOR_IETF; + u32 next_type = EAP_TYPE_NONE; const struct eap_hdr *hdr; const u8 *pos; size_t left; @@ -894,17 +928,23 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, "allowed types", pos + 1, left - 1); eap_sm_process_nak(sm, pos + 1, left - 1); if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && - sm->user->methods[sm->user_eap_method_index].method != - EAP_TYPE_NONE) { + (sm->user->methods[sm->user_eap_method_index].vendor != + EAP_VENDOR_IETF || + sm->user->methods[sm->user_eap_method_index].method != + EAP_TYPE_NONE)) { + next_vendor = sm->user->methods[ + sm->user_eap_method_index].vendor; next_type = sm->user->methods[ sm->user_eap_method_index++].method; - wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", - next_type); + wpa_printf(MSG_DEBUG, + "EAP-PEAP: try EAP vendor %d type 0x%x", + next_vendor, next_type); } else { eap_peap_req_failure(sm, data); + next_vendor = EAP_VENDOR_IETF; next_type = EAP_TYPE_NONE; } - eap_peap_phase2_init(sm, data, next_type); + eap_peap_phase2_init(sm, data, next_vendor, next_type); return; } @@ -929,8 +969,9 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method failed"); eap_peap_req_failure(sm, data); + next_vendor = EAP_VENDOR_IETF; next_type = EAP_TYPE_NONE; - eap_peap_phase2_init(sm, data, next_type); + eap_peap_phase2_init(sm, data, next_vendor, next_type); return; } @@ -942,7 +983,8 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 getKey " "failed"); eap_peap_req_failure(sm, data); - eap_peap_phase2_init(sm, data, EAP_TYPE_NONE); + eap_peap_phase2_init(sm, data, EAP_VENDOR_IETF, + EAP_TYPE_NONE); return; } } @@ -957,6 +999,7 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, "database", sm->identity, sm->identity_len); eap_peap_req_failure(sm, data); + next_vendor = EAP_VENDOR_IETF; next_type = EAP_TYPE_NONE; break; } @@ -967,18 +1010,22 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, eap_peap_state(data, PHASE2_SOH); wpa_printf(MSG_DEBUG, "EAP-PEAP: Try to initialize " "TNC (NAP SOH)"); + next_vendor = EAP_VENDOR_IETF; next_type = EAP_TYPE_NONE; break; } #endif /* EAP_SERVER_TNC */ eap_peap_state(data, PHASE2_METHOD); + next_vendor = sm->user->methods[0].vendor; next_type = sm->user->methods[0].method; sm->user_eap_method_index = 1; - wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); + wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP vendor %d type 0x%x", + next_vendor, next_type); break; case PHASE2_METHOD: eap_peap_req_success(sm, data); + next_vendor = EAP_VENDOR_IETF; next_type = EAP_TYPE_NONE; break; case FAILURE: @@ -989,7 +1036,7 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, break; } - eap_peap_phase2_init(sm, data, next_type); + eap_peap_phase2_init(sm, data, next_vendor, next_type); } @@ -1080,6 +1127,7 @@ static void eap_peap_process_phase2(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success"); if (data->state == SUCCESS_REQ) { eap_peap_state(data, SUCCESS); + eap_peap_valid_session(sm, data); } break; case EAP_CODE_FAILURE: @@ -1133,7 +1181,8 @@ static void eap_peap_process_msg(struct eap_sm *sm, void *priv, break; case PHASE2_START: eap_peap_state(data, PHASE2_ID); - eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY); + eap_peap_phase2_init(sm, data, EAP_VENDOR_IETF, + EAP_TYPE_IDENTITY); break; case PHASE1_ID2: case PHASE2_ID: @@ -1144,6 +1193,7 @@ static void eap_peap_process_msg(struct eap_sm *sm, void *priv, break; case SUCCESS_REQ: eap_peap_state(data, SUCCESS); + eap_peap_valid_session(sm, data); break; case FAILURE_REQ: eap_peap_state(data, FAILURE); @@ -1160,10 +1210,65 @@ static void eap_peap_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_peap_data *data = priv; + const struct wpabuf *buf; + const u8 *pos; + u8 id_len; + if (eap_server_tls_process(sm, &data->ssl, respData, data, EAP_TYPE_PEAP, eap_peap_process_version, - eap_peap_process_msg) < 0) + eap_peap_process_msg) < 0) { eap_peap_state(data, FAILURE); + return; + } + + if (data->state == SUCCESS || + !tls_connection_established(sm->ssl_ctx, data->ssl.conn) || + !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) + return; + + buf = tls_connection_get_success_data(data->ssl.conn); + if (!buf || wpabuf_len(buf) < 2) { + wpa_printf(MSG_DEBUG, + "EAP-PEAP: No success data in resumed session - reject attempt"); + eap_peap_state(data, FAILURE); + return; + } + + pos = wpabuf_head(buf); + if (*pos != EAP_TYPE_PEAP) { + wpa_printf(MSG_DEBUG, + "EAP-PEAP: Resumed session for another EAP type (%u) - reject attempt", + *pos); + eap_peap_state(data, FAILURE); + return; + } + + pos++; + id_len = *pos++; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PEAP: Identity from cached session", + pos, id_len); + os_free(sm->identity); + sm->identity = os_malloc(id_len ? id_len : 1); + if (!sm->identity) { + sm->identity_len = 0; + eap_peap_state(data, FAILURE); + return; + } + + os_memcpy(sm->identity, pos, id_len); + sm->identity_len = id_len; + + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PEAP: Phase2 Identity not found in the user database", + sm->identity, sm->identity_len); + eap_peap_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, + "EAP-PEAP: Resuming previous session - skip Phase2"); + eap_peap_state(data, SUCCESS_REQ); + tls_connection_set_success_data_resumed(data->ssl.conn); } diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c index 943af0d15078d..cb83ff7305bd0 100644 --- a/src/eap_server/eap_server_pwd.c +++ b/src/eap_server/eap_server_pwd.c @@ -10,6 +10,7 @@ #include "common.h" #include "crypto/sha256.h" +#include "crypto/ms_funcs.h" #include "eap_server/eap_i.h" #include "eap_common/eap_pwd_common.h" @@ -24,6 +25,7 @@ struct eap_pwd_data { size_t id_server_len; u8 *password; size_t password_len; + int password_hash; u32 token; u16 group_num; EAP_PWD_group *grp; @@ -112,6 +114,7 @@ static void * eap_pwd_init(struct eap_sm *sm) } data->password_len = sm->user->password_len; 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) { @@ -181,7 +184,8 @@ static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, 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, EAP_PWD_PREP_NONE); + wpabuf_put_u8(data->outbuf, data->password_hash ? EAP_PWD_PREP_MS : + EAP_PWD_PREP_NONE); wpabuf_put_data(data->outbuf, data->id_server, data->id_server_len); } @@ -579,6 +583,10 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm, 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 (payload_len < sizeof(struct eap_pwd_id)) { wpa_printf(MSG_INFO, "EAP-pwd: Invalid ID response"); @@ -610,11 +618,25 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm, "group"); return; } - if (compute_password_element(data->grp, data->group_num, - data->password, data->password_len, - data->id_server, data->id_server_len, - data->id_peer, data->id_peer_len, - (u8 *) &data->token)) { + + if (data->password_hash) { + res = hash_nt_password_hash(data->password, pwhashhash); + if (res) + return; + password = pwhashhash; + password_len = sizeof(pwhashhash); + } else { + password = data->password; + password_len = data->password_len; + } + + res = compute_password_element(data->grp, data->group_num, + password, password_len, + data->id_server, data->id_server_len, + data->id_peer, data->id_peer_len, + (u8 *) &data->token); + os_memset(pwhashhash, 0, sizeof(pwhashhash)); + if (res) { wpa_printf(MSG_INFO, "EAP-PWD (server): unable to compute " "PWE"); return; @@ -634,9 +656,21 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, BIGNUM *x = NULL, *y = NULL, *cofactor = NULL; 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); + + 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->peer_scalar = BN_new()) == NULL) || ((data->k = BN_new()) == NULL) || ((cofactor = BN_new()) == NULL) || @@ -752,6 +786,13 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; int offset; + if (payload_len != SHA256_MAC_LEN) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Confirm payload length %u (expected %u)", + (unsigned int) payload_len, SHA256_MAC_LEN); + goto fin; + } + /* build up the ciphersuite: group | random_function | prf */ grp = htons(data->group_num); ptr = (u8 *) &cs; @@ -901,17 +942,28 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv, * the first fragment has a total length */ if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { + if (len < 2) { + wpa_printf(MSG_DEBUG, + "EAP-pwd: Frame too short to contain Total-Length field"); + return; + } tot_len = WPA_GET_BE16(pos); wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments, total " "length = %d", tot_len); if (tot_len > 15000) return; + if (data->inbuf) { + wpa_printf(MSG_DEBUG, + "EAP-pwd: Unexpected new fragment start when previous fragment is still in use"); + return; + } data->inbuf = wpabuf_alloc(tot_len); if (data->inbuf == NULL) { wpa_printf(MSG_INFO, "EAP-pwd: Out of memory to " "buffer fragments!"); return; } + data->in_frag_pos = 0; pos += sizeof(u16); len -= sizeof(u16); } diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c index 58cfe8ac64a02..bd18a4ba654ce 100644 --- a/src/eap_server/eap_server_tls.c +++ b/src/eap_server/eap_server_tls.c @@ -48,6 +48,23 @@ static void eap_tls_state(struct eap_tls_data *data, int state) eap_tls_state_txt(data->state), eap_tls_state_txt(state)); data->state = state; + if (state == FAILURE) + tls_connection_remove_session(data->ssl.conn); +} + + +static void eap_tls_valid_session(struct eap_sm *sm, struct eap_tls_data *data) +{ + struct wpabuf *buf; + + if (!sm->tls_session_lifetime) + return; + + buf = wpabuf_alloc(1); + if (!buf) + return; + wpabuf_put_u8(buf, data->eap_type); + tls_connection_set_success_data(data->ssl.conn, buf); } @@ -60,7 +77,7 @@ static void * eap_tls_init(struct eap_sm *sm) return NULL; data->state = START; - if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 1, EAP_TYPE_TLS)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_reset(sm, data); return NULL; @@ -82,7 +99,7 @@ static void * eap_unauth_tls_init(struct eap_sm *sm) return NULL; data->state = START; - if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_UNAUTH_TLS_TYPE)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_reset(sm, data); return NULL; @@ -104,7 +121,8 @@ static void * eap_wfa_unauth_tls_init(struct eap_sm *sm) return NULL; data->state = START; - if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 0, + EAP_WFA_UNAUTH_TLS_TYPE)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_reset(sm, data); return NULL; @@ -183,6 +201,7 @@ check_established: * fragments waiting to be sent out. */ wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); eap_tls_state(data, SUCCESS); + eap_tls_valid_session(sm, data); } return res; @@ -234,10 +253,41 @@ static void eap_tls_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_tls_data *data = priv; + const struct wpabuf *buf; + const u8 *pos; + if (eap_server_tls_process(sm, &data->ssl, respData, data, data->eap_type, NULL, eap_tls_process_msg) < - 0) + 0) { eap_tls_state(data, FAILURE); + return; + } + + if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) || + !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) + return; + + buf = tls_connection_get_success_data(data->ssl.conn); + if (!buf || wpabuf_len(buf) < 1) { + wpa_printf(MSG_DEBUG, + "EAP-TLS: No success data in resumed session - reject attempt"); + eap_tls_state(data, FAILURE); + return; + } + + pos = wpabuf_head(buf); + if (*pos != data->eap_type) { + wpa_printf(MSG_DEBUG, + "EAP-TLS: Resumed session for another EAP type (%u) - reject attempt", + *pos); + eap_tls_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, + "EAP-TLS: Resuming previous session"); + eap_tls_state(data, SUCCESS); + tls_connection_set_success_data_resumed(data->ssl.conn); } diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c index 56916c45ac69a..05677b70e8873 100644 --- a/src/eap_server/eap_server_tls_common.c +++ b/src/eap_server/eap_server_tls_common.c @@ -44,8 +44,11 @@ static void eap_server_tls_log_cb(void *ctx, const char *msg) int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, - int verify_peer) + int verify_peer, int eap_type) { + u8 session_ctx[8]; + unsigned int flags = 0; + if (sm->ssl_ctx == NULL) { wpa_printf(MSG_ERROR, "TLS context not initialized - cannot use TLS-based EAP method"); return -1; @@ -68,7 +71,13 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_TLS_INTERNAL */ - if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) { + if (eap_type != EAP_TYPE_FAST) + flags |= TLS_CONN_DISABLE_SESSION_TICKET; + os_memcpy(session_ctx, "hostapd", 7); + session_ctx[7] = (u8) eap_type; + if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer, + flags, session_ctx, + sizeof(session_ctx))) { wpa_printf(MSG_INFO, "SSL: Failed to configure verification " "of TLS peer certificate"); tls_connection_deinit(sm->ssl_ctx, data->conn); @@ -100,43 +109,19 @@ 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) { - struct tls_keys keys; - u8 *rnd = NULL, *out; + u8 *out; out = os_malloc(len); if (out == NULL) return NULL; - if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) == - 0) - return out; - - if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) - goto fail; - - if (keys.client_random == NULL || keys.server_random == NULL || - keys.master_key == NULL) - goto fail; - - rnd = os_malloc(keys.client_random_len + keys.server_random_len); - if (rnd == NULL) - goto fail; - os_memcpy(rnd, keys.client_random, keys.client_random_len); - os_memcpy(rnd + keys.client_random_len, keys.server_random, - keys.server_random_len); - - if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, - label, rnd, keys.client_random_len + - keys.server_random_len, out, len)) - goto fail; + if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, 0, + out, len)) { + os_free(out); + return NULL; + } - os_free(rnd); return out; - -fail: - os_free(out); - os_free(rnd); - return NULL; } @@ -157,10 +142,10 @@ u8 * eap_server_tls_derive_session_id(struct eap_sm *sm, struct eap_ssl_data *data, u8 eap_type, size_t *len) { - struct tls_keys keys; + struct tls_random keys; u8 *out; - if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys)) return NULL; if (keys.client_random == NULL || keys.server_random == NULL) diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c index 12a31b07a63b4..53ffa1ec6785a 100644 --- a/src/eap_server/eap_server_ttls.c +++ b/src/eap_server/eap_server_ttls.c @@ -71,6 +71,36 @@ static void eap_ttls_state(struct eap_ttls_data *data, int state) eap_ttls_state_txt(data->state), eap_ttls_state_txt(state)); data->state = state; + if (state == FAILURE) + tls_connection_remove_session(data->ssl.conn); +} + + +static void eap_ttls_valid_session(struct eap_sm *sm, + struct eap_ttls_data *data) +{ + struct wpabuf *buf; + + if (!sm->tls_session_lifetime) + return; + + buf = wpabuf_alloc(1 + 1 + sm->identity_len); + if (!buf) + return; + wpabuf_put_u8(buf, EAP_TYPE_TTLS); + if (sm->identity) { + u8 id_len; + + if (sm->identity_len <= 255) + id_len = sm->identity_len; + else + id_len = 255; + wpabuf_put_u8(buf, id_len); + wpabuf_put_data(buf, sm->identity, id_len); + } else { + wpabuf_put_u8(buf, 0); + } + tls_connection_set_success_data(data->ssl.conn, buf); } @@ -317,7 +347,7 @@ static void * eap_ttls_init(struct eap_sm *sm) data->ttls_version = EAP_TTLS_VERSION; data->state = START; - if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_TTLS)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); eap_ttls_reset(sm, data); return NULL; @@ -518,6 +548,7 @@ static void eap_ttls_process_phase2_pap(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password"); eap_ttls_state(data, SUCCESS); + eap_ttls_valid_session(sm, data); } @@ -576,6 +607,7 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password"); eap_ttls_state(data, SUCCESS); + eap_ttls_valid_session(sm, data); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password"); eap_ttls_state(data, FAILURE); @@ -618,6 +650,12 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, return; } +#ifdef CONFIG_TESTING_OPTIONS + eap_server_mschap_rx_callback(sm, "TTLS-MSCHAP", + sm->identity, sm->identity_len, + challenge, response + 2 + 24); +#endif /* CONFIG_TESTING_OPTIONS */ + if (os_memcmp_const(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN) != 0 || response[0] != chal[EAP_TTLS_MSCHAP_CHALLENGE_LEN]) { @@ -637,6 +675,7 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, if (os_memcmp_const(nt_response, response + 2 + 24, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response"); eap_ttls_state(data, SUCCESS); + eap_ttls_valid_session(sm, data); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response"); wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received", @@ -740,6 +779,18 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, } rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8; +#ifdef CONFIG_TESTING_OPTIONS + { + u8 challenge2[8]; + + if (challenge_hash(peer_challenge, auth_challenge, + username, username_len, challenge2) == 0) { + eap_server_mschap_rx_callback(sm, "TTLS-MSCHAPV2", + username, username_len, + challenge2, rx_resp); + } + } +#endif /* CONFIG_TESTING_OPTIONS */ if (os_memcmp_const(nt_response, rx_resp, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct " "NT-Response"); @@ -888,6 +939,7 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, break; case PHASE2_METHOD: eap_ttls_state(data, SUCCESS); + eap_ttls_valid_session(sm, data); break; case FAILURE: break; @@ -1111,6 +1163,7 @@ static void eap_ttls_process_msg(struct eap_sm *sm, void *priv, wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged response"); eap_ttls_state(data, SUCCESS); + eap_ttls_valid_session(sm, data); } else if (!data->mschapv2_resp_ok) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged error"); @@ -1137,10 +1190,64 @@ static void eap_ttls_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_ttls_data *data = priv; + const struct wpabuf *buf; + const u8 *pos; + u8 id_len; + if (eap_server_tls_process(sm, &data->ssl, respData, data, EAP_TYPE_TTLS, eap_ttls_process_version, - eap_ttls_process_msg) < 0) + eap_ttls_process_msg) < 0) { + eap_ttls_state(data, FAILURE); + return; + } + + if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) || + !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) + return; + + buf = tls_connection_get_success_data(data->ssl.conn); + if (!buf || wpabuf_len(buf) < 1) { + wpa_printf(MSG_DEBUG, + "EAP-TTLS: No success data in resumed session - reject attempt"); + eap_ttls_state(data, FAILURE); + return; + } + + pos = wpabuf_head(buf); + if (*pos != EAP_TYPE_TTLS) { + wpa_printf(MSG_DEBUG, + "EAP-TTLS: Resumed session for another EAP type (%u) - reject attempt", + *pos); + eap_ttls_state(data, FAILURE); + return; + } + + pos++; + id_len = *pos++; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: Identity from cached session", + pos, id_len); + os_free(sm->identity); + sm->identity = os_malloc(id_len ? id_len : 1); + if (!sm->identity) { + sm->identity_len = 0; eap_ttls_state(data, FAILURE); + return; + } + + os_memcpy(sm->identity, pos, id_len); + sm->identity_len = id_len; + + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not found in the user database", + sm->identity, sm->identity_len); + eap_ttls_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, + "EAP-TTLS: Resuming previous session - skip Phase2"); + eap_ttls_state(data, SUCCESS); + tls_connection_set_success_data_resumed(data->ssl.conn); } diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h index ddf90b859ee40..dc943eb207d74 100644 --- a/src/eap_server/eap_tls_common.h +++ b/src/eap_server/eap_tls_common.h @@ -70,7 +70,7 @@ struct eap_ssl_data { struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, u8 code, u8 identifier); int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, - int verify_peer); + 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); diff --git a/src/eapol_auth/Makefile b/src/eapol_auth/Makefile index adfd3dfd5b9be..7b927a1277310 100644 --- a/src/eapol_auth/Makefile +++ b/src/eapol_auth/Makefile @@ -1,8 +1,16 @@ -all: - @echo Nothing to be made. +all: libeapol_auth.a clean: - rm -f *~ *.o *.d *.gcno *.gcda *.gcov + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeapol_auth.a install: @echo Nothing to be made. + +include ../lib.rules + +LIB_OBJS = eapol_auth_sm.o eapol_auth_dump.o + +libeapol_auth.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c index 0df6eb56416b6..ff33d286223fd 100644 --- a/src/eapol_auth/eapol_auth_sm.c +++ b/src/eapol_auth/eapol_auth_sm.c @@ -1,6 +1,6 @@ /* * IEEE 802.1X-2004 Authenticator - EAPOL state machine - * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -22,7 +22,7 @@ #define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X" #define STATE_MACHINE_ADDR sm->addr -static struct eapol_callbacks eapol_cb; +static const struct eapol_callbacks eapol_cb; /* EAPOL state machines are described in IEEE Std 802.1X-2004, Chap. 8.2 */ @@ -198,6 +198,18 @@ SM_STATE(AUTH_PAE, INITIALIZE) { SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae); sm->portMode = Auto; + + /* + * Clearing keyRun here is not specified in IEEE Std 802.1X-2004, but + * it looks like this would be logical thing to do here since the + * EAPOL-Key exchange is not possible in this state. It is possible to + * get here on disconnection event without advancing to the + * AUTHENTICATING state to clear keyRun before the IEEE 802.11 RSN + * authenticator state machine runs and that may advance from + * AUTHENTICATION2 to INITPMK if keyRun = TRUE has been left from the + * last association. This can be avoided by clearing keyRun here. + */ + sm->keyRun = FALSE; } @@ -835,6 +847,7 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, eap_conf.server_id = eapol->conf.server_id; 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; sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); if (sm->eap == NULL) { eapol_auth_free(sm); @@ -1056,7 +1069,7 @@ static int eapol_sm_erp_add_key(void *ctx, struct eap_server_erp_key *erp) } -static struct eapol_callbacks eapol_cb = +static const struct eapol_callbacks eapol_cb = { eapol_sm_get_eap_user, eapol_sm_get_eap_req_id_text, @@ -1080,6 +1093,87 @@ int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx) } +void eapol_auth_reauthenticate(struct eapol_state_machine *sm) +{ + wpa_printf(MSG_DEBUG, "EAPOL: External reauthentication trigger for " + MACSTR, MAC2STR(sm->addr)); + sm->reAuthenticate = TRUE; + eapol_auth_step(sm); +} + + +int eapol_auth_set_conf(struct eapol_state_machine *sm, const char *param, + const char *value) +{ + wpa_printf(MSG_DEBUG, "EAPOL: External configuration operation for " + MACSTR " - param=%s value=%s", + MAC2STR(sm->addr), param, value); + + if (os_strcasecmp(param, "AdminControlledDirections") == 0) { + if (os_strcmp(value, "Both") == 0) + sm->adminControlledDirections = Both; + else if (os_strcmp(value, "In") == 0) + sm->adminControlledDirections = In; + else + return -1; + eapol_auth_step(sm); + return 0; + } + + if (os_strcasecmp(param, "AdminControlledPortControl") == 0) { + if (os_strcmp(value, "ForceAuthorized") == 0) + sm->portControl = ForceAuthorized; + else if (os_strcmp(value, "ForceUnauthorized") == 0) + sm->portControl = ForceUnauthorized; + else if (os_strcmp(value, "Auto") == 0) + sm->portControl = Auto; + else + return -1; + eapol_auth_step(sm); + return 0; + } + + if (os_strcasecmp(param, "quietPeriod") == 0) { + sm->quietPeriod = atoi(value); + return 0; + } + + if (os_strcasecmp(param, "serverTimeout") == 0) { + sm->serverTimeout = atoi(value); + return 0; + } + + if (os_strcasecmp(param, "reAuthPeriod") == 0) { + sm->reAuthPeriod = atoi(value); + return 0; + } + + if (os_strcasecmp(param, "reAuthEnabled") == 0) { + if (os_strcmp(value, "TRUE") == 0) + sm->reAuthEnabled = TRUE; + else if (os_strcmp(value, "FALSE") == 0) + sm->reAuthEnabled = FALSE; + else + return -1; + eapol_auth_step(sm); + return 0; + } + + if (os_strcasecmp(param, "KeyTransmissionEnabled") == 0) { + if (os_strcmp(value, "TRUE") == 0) + sm->keyTxEnabled = TRUE; + else if (os_strcmp(value, "FALSE") == 0) + sm->keyTxEnabled = FALSE; + else + return -1; + eapol_auth_step(sm); + return 0; + } + + return -1; +} + + static int eapol_auth_conf_clone(struct eapol_auth_config *dst, struct eapol_auth_config *src) { @@ -1148,6 +1242,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; return 0; diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h index ebed19adefc7b..e1974e4354dac 100644 --- a/src/eapol_auth/eapol_auth_sm.h +++ b/src/eapol_auth/eapol_auth_sm.h @@ -1,6 +1,6 @@ /* * IEEE 802.1X-2004 Authenticator - EAPOL state machine - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -27,6 +27,7 @@ struct eapol_auth_config { int erp_send_reauth_start; char *erp_domain; /* a copy of this will be allocated */ int erp; /* Whether ERP is enabled on authentication server */ + unsigned int tls_session_lifetime; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; @@ -94,5 +95,8 @@ void eapol_auth_step(struct eapol_state_machine *sm); int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, size_t buflen); int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx); +void eapol_auth_reauthenticate(struct eapol_state_machine *sm); +int eapol_auth_set_conf(struct eapol_state_machine *sm, const char *param, + const char *value); #endif /* EAPOL_AUTH_SM_H */ diff --git a/src/eapol_supp/Makefile b/src/eapol_supp/Makefile index adfd3dfd5b9be..80db9d48689ee 100644 --- a/src/eapol_supp/Makefile +++ b/src/eapol_supp/Makefile @@ -1,8 +1,18 @@ -all: - @echo Nothing to be made. +all: libeapol_supp.a clean: - rm -f *~ *.o *.d *.gcno *.gcda *.gcov + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeapol_supp.a install: @echo Nothing to be made. + +include ../lib.rules + +CFLAGS += -DIEEE8021X_EAPOL + +LIB_OBJS = eapol_supp_sm.o + +libeapol_supp.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index 9cc234a82b09e..09cf4f6b92225 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -244,7 +244,8 @@ SM_STATE(SUPP_PAE, DISCONNECTED) SM_STATE(SUPP_PAE, CONNECTING) { - int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING; + int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING || + sm->SUPP_PAE_state == SUPP_PAE_HELD; SM_ENTRY(SUPP_PAE, CONNECTING); if (sm->eapTriggerStart) @@ -653,7 +654,9 @@ static void eapol_sm_processKey(struct eapol_sm *sm) struct ieee802_1x_eapol_key *key; struct eap_key_data keydata; u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32]; +#ifndef CONFIG_NO_RC4 u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN]; +#endif /* CONFIG_NO_RC4 */ int key_len, res, sign_key_len, encr_key_len; u16 rx_key_length; size_t plen; @@ -747,6 +750,13 @@ static void eapol_sm_processKey(struct eapol_sm *sm) return; } if (key_len == rx_key_length) { +#ifdef CONFIG_NO_RC4 + if (encr_key_len) { + /* otherwise unused */ + } + wpa_printf(MSG_ERROR, "EAPOL: RC4 not supported in the build"); + return; +#else /* CONFIG_NO_RC4 */ os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN); os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key, encr_key_len); @@ -755,6 +765,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm) datakey, key_len); wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key", datakey, key_len); +#endif /* CONFIG_NO_RC4 */ } else if (key_len == 0) { /* * IEEE 802.1X-2004 specifies that least significant Key Length @@ -1997,7 +2008,7 @@ static void eapol_sm_set_anon_id(void *ctx, const u8 *id, size_t len) } -static struct eapol_callbacks eapol_cb = +static const struct eapol_callbacks eapol_cb = { eapol_sm_get_config, eapol_sm_get_bool, diff --git a/src/fst/Makefile b/src/fst/Makefile new file mode 100644 index 0000000000000..9c41962fd7e16 --- /dev/null +++ b/src/fst/Makefile @@ -0,0 +1,8 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/src/fst/fst.c b/src/fst/fst.c new file mode 100644 index 0000000000000..2880870213e61 --- /dev/null +++ b/src/fst/fst.c @@ -0,0 +1,225 @@ +/* + * FST module implementation + * Copyright (c) 2014, 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 "fst/fst.h" +#include "fst/fst_internal.h" +#include "fst/fst_defs.h" +#include "fst/fst_ctrl_iface.h" + +struct dl_list fst_global_ctrls_list; + + +static void fst_ctrl_iface_notify_peer_state_change(struct fst_iface *iface, + Boolean connected, + const u8 *peer_addr) +{ + union fst_event_extra extra; + + extra.peer_state.connected = connected; + os_strlcpy(extra.peer_state.ifname, fst_iface_get_name(iface), + sizeof(extra.peer_state.ifname)); + os_memcpy(extra.peer_state.addr, peer_addr, ETH_ALEN); + + foreach_fst_ctrl_call(on_event, EVENT_PEER_STATE_CHANGED, + iface, NULL, &extra); +} + + +struct fst_iface * fst_attach(const char *ifname, const u8 *own_addr, + const struct fst_wpa_obj *iface_obj, + const struct fst_iface_cfg *cfg) +{ + struct fst_group *g; + struct fst_group *group = NULL; + struct fst_iface *iface = NULL; + Boolean new_group = FALSE; + + WPA_ASSERT(ifname != NULL); + WPA_ASSERT(iface_obj != NULL); + WPA_ASSERT(cfg != NULL); + + foreach_fst_group(g) { + if (os_strcmp(cfg->group_id, fst_group_get_id(g)) == 0) { + group = g; + break; + } + } + + if (!group) { + group = fst_group_create(cfg->group_id); + if (!group) { + fst_printf(MSG_ERROR, "%s: FST group cannot be created", + cfg->group_id); + return NULL; + } + new_group = TRUE; + } + + iface = fst_iface_create(group, ifname, own_addr, iface_obj, cfg); + if (!iface) { + fst_printf_group(group, MSG_ERROR, "cannot create iface for %s", + ifname); + if (new_group) + fst_group_delete(group); + return NULL; + } + + fst_group_attach_iface(group, iface); + fst_group_update_ie(group); + + foreach_fst_ctrl_call(on_iface_added, iface); + + fst_printf_iface(iface, MSG_DEBUG, + "iface attached to group %s (prio=%d, llt=%d)", + cfg->group_id, cfg->priority, cfg->llt); + + return iface; +} + + +void fst_detach(struct fst_iface *iface) +{ + struct fst_group *group = fst_iface_get_group(iface); + + fst_printf_iface(iface, MSG_DEBUG, "iface detached from group %s", + fst_group_get_id(group)); + fst_session_global_on_iface_detached(iface); + foreach_fst_ctrl_call(on_iface_removed, iface); + fst_group_detach_iface(group, iface); + fst_iface_delete(iface); + fst_group_update_ie(group); + fst_group_delete_if_empty(group); +} + + +int fst_global_init(void) +{ + dl_list_init(&fst_global_groups_list); + dl_list_init(&fst_global_ctrls_list); + fst_session_global_init(); + return 0; +} + + +void fst_global_deinit(void) +{ + struct fst_group *group; + struct fst_ctrl_handle *h; + + fst_session_global_deinit(); + while ((group = fst_first_group()) != NULL) + fst_group_delete(group); + while ((h = dl_list_first(&fst_global_ctrls_list, + struct fst_ctrl_handle, + global_ctrls_lentry))) + fst_global_del_ctrl(h); +} + + +struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl) +{ + struct fst_ctrl_handle *h; + + if (!ctrl) + return NULL; + + h = os_zalloc(sizeof(*h)); + if (!h) + return NULL; + + if (ctrl->init && ctrl->init()) { + os_free(h); + return NULL; + } + + h->ctrl = *ctrl; + dl_list_add_tail(&fst_global_ctrls_list, &h->global_ctrls_lentry); + + return h; +} + + +void fst_global_del_ctrl(struct fst_ctrl_handle *h) +{ + dl_list_del(&h->global_ctrls_lentry); + if (h->ctrl.deinit) + h->ctrl.deinit(); + os_free(h); +} + + +void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt, + size_t len) +{ + if (fst_iface_is_connected(iface, mgmt->sa)) + fst_session_on_action_rx(iface, mgmt, len); + else + wpa_printf(MSG_DEBUG, + "FST: Ignore FST Action frame - no FST connection with " + MACSTR, MAC2STR(mgmt->sa)); +} + + +void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr) +{ + if (is_zero_ether_addr(addr)) + return; + +#ifndef HOSTAPD + fst_group_update_ie(fst_iface_get_group(iface)); +#endif /* HOSTAPD */ + + fst_printf_iface(iface, MSG_DEBUG, MACSTR " became connected", + MAC2STR(addr)); + + fst_ctrl_iface_notify_peer_state_change(iface, TRUE, addr); +} + + +void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr) +{ + if (is_zero_ether_addr(addr)) + return; + +#ifndef HOSTAPD + fst_group_update_ie(fst_iface_get_group(iface)); +#endif /* HOSTAPD */ + + fst_printf_iface(iface, MSG_DEBUG, MACSTR " became disconnected", + MAC2STR(addr)); + + fst_ctrl_iface_notify_peer_state_change(iface, FALSE, addr); +} + + +Boolean fst_are_ifaces_aggregated(struct fst_iface *iface1, + struct fst_iface *iface2) +{ + return fst_iface_get_group(iface1) == fst_iface_get_group(iface2); +} + + +enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode) +{ + switch (mode) { + case HOSTAPD_MODE_IEEE80211B: + case HOSTAPD_MODE_IEEE80211G: + return MB_BAND_ID_WIFI_2_4GHZ; + case HOSTAPD_MODE_IEEE80211A: + return MB_BAND_ID_WIFI_5GHZ; + case HOSTAPD_MODE_IEEE80211AD: + return MB_BAND_ID_WIFI_60GHZ; + default: + WPA_ASSERT(0); + return MB_BAND_ID_WIFI_2_4GHZ; + } +} diff --git a/src/fst/fst.h b/src/fst/fst.h new file mode 100644 index 0000000000000..0c0e435b974b5 --- /dev/null +++ b/src/fst/fst.h @@ -0,0 +1,296 @@ +/* + * FST module - interface definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_H +#define FST_H + +#ifdef CONFIG_FST + +#include "common/defs.h" +#include "fst/fst_ctrl_iface.h" + +/* FST module hostap integration API */ + +#define US_IN_MS 1000 +#define LLT_UNIT_US 32 /* See 10.32.2.2 Transitioning between states */ + +#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * US_IN_MS / LLT_UNIT_US) +#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * LLT_UNIT_US / US_IN_MS) + +#define FST_MAX_LLT_MS FST_LLT_VAL_TO_MS(-1) +#define FST_MAX_PRIO_VALUE ((u8) -1) +#define FST_MAX_GROUP_ID_LEN IFNAMSIZ + +#define FST_DEFAULT_LLT_CFG_VALUE 50 + +struct hostapd_hw_modes; +struct ieee80211_mgmt; +struct fst_iface; +struct fst_group; +struct fst_session; +struct fst_get_peer_ctx; +struct fst_ctrl_handle; + +struct fst_wpa_obj { + void *ctx; + + /** + * get_bssid - Get BSSID of the interface + * @ctx: User context %ctx + * Returns: BSSID for success, %NULL for failure. + * + * NOTE: For AP it returns the own BSSID, while for STA - the BSSID of + * the associated AP. + */ + const u8 * (*get_bssid)(void *ctx); + + /** + * get_channel_info - Get current channel info + * @ctx: User context %ctx + * @hw_mode: OUT, current HW mode + * @channel: OUT, current channel + */ + void (*get_channel_info)(void *ctx, enum hostapd_hw_mode *hw_mode, + u8 *channel); + + /** + * get_hw_modes - Get hardware modes + * @ctx: User context %ctx + * @modes: OUT, pointer on array of hw modes + * + * Returns: Number of hw modes available. + */ + int (*get_hw_modes)(void *ctx, struct hostapd_hw_modes **modes); + + /** + * set_ies - Set interface's MB IE + * @ctx: User context %ctx + * @fst_ies: MB IE buffer (owned by FST module) + */ + void (*set_ies)(void *ctx, const struct wpabuf *fst_ies); + + /** + * send_action - Send FST Action frame via the interface + * @ctx: User context %ctx + * @addr: Address of the destination STA + * @data: Action frame buffer + * Returns: 0 for success, negative error code for failure. + */ + int (*send_action)(void *ctx, const u8 *addr, struct wpabuf *data); + + /** + * get_mb_ie - Get last MB IE received from STA + * @ctx: User context %ctx + * @addr: Address of the STA + * Returns: MB IE buffer, %NULL if no MB IE received from the STA + */ + const struct wpabuf * (*get_mb_ie)(void *ctx, const u8 *addr); + + /** + * update_mb_ie - Update last MB IE received from STA + * @ctx: User context %ctx + * @addr: Address of the STA + * @buf: Buffer that contains the MB IEs data + * @size: Size of data in %buf + */ + void (*update_mb_ie)(void *ctx, const u8 *addr, + const u8 *buf, size_t size); + + /** + * get_peer_first - Get MAC address of the 1st connected STA + * @ctx: User context %ctx + * @get_ctx: Context to be used for %get_peer_next call + * @mb_only: %TRUE if only multi-band capable peer should be reported + * Returns: Address of the 1st connected STA, %NULL if no STAs connected + */ + const u8 * (*get_peer_first)(void *ctx, + struct fst_get_peer_ctx **get_ctx, + Boolean mb_only); + /** + * get_peer_next - Get MAC address of the next connected STA + * @ctx: User context %ctx + * @get_ctx: Context received from %get_peer_first or previous + * %get_peer_next call + * @mb_only: %TRUE if only multi-band capable peer should be reported + * Returns: Address of the next connected STA, %NULL if no more STAs + * connected + */ + const u8 * (*get_peer_next)(void *ctx, + struct fst_get_peer_ctx **get_ctx, + Boolean mb_only); +}; + +/** + * fst_global_init - Global FST module initiator + * Returns: 0 for success, negative error code for failure. + * Note: The purpose of this function is to allocate and initiate global + * FST module data structures (linked lists, static data etc.) + * This function should be called prior to the 1st %fst_attach call. + */ +int fst_global_init(void); + +/** + * fst_global_deinit - Global FST module de-initiator + * Note: The purpose of this function is to deallocate and de-initiate global + * FST module data structures (linked lists, static data etc.) + */ +void fst_global_deinit(void); + +/** + * struct fst_ctrl - Notification interface for FST module + */ +struct fst_ctrl { + /** + * init - Initialize the notification interface + * Returns: 0 for success, negative error code for failure. + */ + int (*init)(void); + + /** + * deinit - Deinitialize the notification interface + */ + void (*deinit)(void); + + /** + * on_group_created - Notify about FST group creation + * Returns: 0 for success, negative error code for failure. + */ + int (*on_group_created)(struct fst_group *g); + + /** + * on_group_deleted - Notify about FST group deletion + */ + void (*on_group_deleted)(struct fst_group *g); + + /** + * on_iface_added - Notify about interface addition + * Returns: 0 for success, negative error code for failure. + */ + int (*on_iface_added)(struct fst_iface *i); + + /** + * on_iface_removed - Notify about interface removal + */ + void (*on_iface_removed)(struct fst_iface *i); + + /** + * on_session_added - Notify about FST session addition + * Returns: 0 for success, negative error code for failure. + */ + int (*on_session_added)(struct fst_session *s); + + /** + * on_session_removed - Notify about FST session removal + */ + void (*on_session_removed)(struct fst_session *s); + + /** + * on_event - Notify about FST event + * @event_type: Event type + * @i: Interface object that relates to the event or NULL + * @g: Group object that relates to the event or NULL + * @extra - Event specific data (see fst_ctrl_iface.h for more info) + */ + void (*on_event)(enum fst_event_type event_type, struct fst_iface *i, + struct fst_session *s, + const union fst_event_extra *extra); +}; + +struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl); +void fst_global_del_ctrl(struct fst_ctrl_handle *h); + +/** + * NOTE: These values have to be read from configuration file + */ +struct fst_iface_cfg { + char group_id[FST_MAX_GROUP_ID_LEN + 1]; + u8 priority; + u32 llt; +}; + +/** + * fst_attach - Attach interface to an FST group according to configuration read + * @ifname: Interface name + * @own_addr: Own interface MAC address + * @iface_obj: Callbacks to be used by FST module to communicate with + * hostapd/wpa_supplicant + * @cfg: FST-related interface configuration read from the configuration file + * Returns: FST interface object for success, %NULL for failure. + */ +struct fst_iface * fst_attach(const char *ifname, + const u8 *own_addr, + const struct fst_wpa_obj *iface_obj, + const struct fst_iface_cfg *cfg); + +/** + * fst_detach - Detach an interface + * @iface: FST interface object + */ +void fst_detach(struct fst_iface *iface); + +/* FST module inputs */ +/** + * fst_rx_action - FST Action frames handler + * @iface: FST interface object + * @mgmt: Action frame arrived + * @len: Action frame length + */ +void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt, + size_t len); + +/** + * fst_notify_peer_connected - FST STA connect handler + * @iface: FST interface object + * @addr: Address of the connected STA + */ +void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr); + +/** + * fst_notify_peer_disconnected - FST STA disconnect handler + * @iface: FST interface object + * @addr: Address of the disconnected STA + */ +void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr); + +/* FST module auxiliary routines */ + +/** + * fst_are_ifaces_aggregated - Determines whether 2 interfaces belong to the + * same FST group + * @iface1: 1st FST interface object + * @iface1: 2nd FST interface object + * + * Returns: %TRUE if the interfaces belong to the same FST group, + * %FALSE otherwise + */ +Boolean fst_are_ifaces_aggregated(struct fst_iface *iface1, + struct fst_iface *iface2); + +#else /* CONFIG_FST */ + +static inline int fst_global_init(void) +{ + return 0; +} + +static inline int fst_global_start(void) +{ + return 0; +} + +static inline void fst_global_stop(void) +{ +} + +static inline void fst_global_deinit(void) +{ +} + +#endif /* CONFIG_FST */ + +#endif /* FST_H */ diff --git a/src/fst/fst_ctrl_aux.c b/src/fst/fst_ctrl_aux.c new file mode 100644 index 0000000000000..dc7b2a7d72021 --- /dev/null +++ b/src/fst/fst_ctrl_aux.c @@ -0,0 +1,69 @@ +/* + * FST module implementation + * Copyright (c) 2014, 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/defs.h" +#include "fst_ctrl_defs.h" +#include "fst_ctrl_aux.h" + + +static const char *session_event_names[] = { + [EVENT_FST_ESTABLISHED] FST_PVAL_EVT_TYPE_ESTABLISHED, + [EVENT_FST_SETUP] FST_PVAL_EVT_TYPE_SETUP, + [EVENT_FST_SESSION_STATE_CHANGED] FST_PVAL_EVT_TYPE_SESSION_STATE, +}; + +static const char *reason_names[] = { + [REASON_TEARDOWN] FST_CS_PVAL_REASON_TEARDOWN, + [REASON_SETUP] FST_CS_PVAL_REASON_SETUP, + [REASON_SWITCH] FST_CS_PVAL_REASON_SWITCH, + [REASON_STT] FST_CS_PVAL_REASON_STT, + [REASON_REJECT] FST_CS_PVAL_REASON_REJECT, + [REASON_ERROR_PARAMS] FST_CS_PVAL_REASON_ERROR_PARAMS, + [REASON_RESET] FST_CS_PVAL_REASON_RESET, + [REASON_DETACH_IFACE] FST_CS_PVAL_REASON_DETACH_IFACE, +}; + +static const char *session_state_names[] = { + [FST_SESSION_STATE_INITIAL] FST_CS_PVAL_STATE_INITIAL, + [FST_SESSION_STATE_SETUP_COMPLETION] FST_CS_PVAL_STATE_SETUP_COMPLETION, + [FST_SESSION_STATE_TRANSITION_DONE] FST_CS_PVAL_STATE_TRANSITION_DONE, + [FST_SESSION_STATE_TRANSITION_CONFIRMED] + FST_CS_PVAL_STATE_TRANSITION_CONFIRMED, +}; + + +/* helpers */ +const char * fst_get_str_name(unsigned index, const char *names[], + size_t names_size) +{ + if (index >= names_size || !names[index]) + return FST_NAME_UNKNOWN; + return names[index]; +} + + +const char * fst_session_event_type_name(enum fst_event_type event) +{ + return fst_get_str_name(event, session_event_names, + ARRAY_SIZE(session_event_names)); +} + + +const char * fst_reason_name(enum fst_reason reason) +{ + return fst_get_str_name(reason, reason_names, ARRAY_SIZE(reason_names)); +} + + +const char * fst_session_state_name(enum fst_session_state state) +{ + return fst_get_str_name(state, session_state_names, + ARRAY_SIZE(session_state_names)); +} diff --git a/src/fst/fst_ctrl_aux.h b/src/fst/fst_ctrl_aux.h new file mode 100644 index 0000000000000..e2133f5062bd1 --- /dev/null +++ b/src/fst/fst_ctrl_aux.h @@ -0,0 +1,91 @@ +/* + * FST module - miscellaneous definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_CTRL_AUX_H +#define FST_CTRL_AUX_H + +#include "common/defs.h" + +/* FST module control interface API */ +#define FST_INVALID_SESSION_ID ((u32) -1) +#define FST_MAX_GROUP_ID_SIZE 32 +#define FST_MAX_INTERFACE_SIZE 32 + +enum fst_session_state { + FST_SESSION_STATE_INITIAL, + FST_SESSION_STATE_SETUP_COMPLETION, + FST_SESSION_STATE_TRANSITION_DONE, + FST_SESSION_STATE_TRANSITION_CONFIRMED, + FST_SESSION_STATE_LAST +}; + +enum fst_event_type { + EVENT_FST_IFACE_STATE_CHANGED, /* An interface has been either attached + * to or detached from an FST group */ + EVENT_FST_ESTABLISHED, /* FST Session has been established */ + EVENT_FST_SETUP, /* FST Session request received */ + EVENT_FST_SESSION_STATE_CHANGED,/* FST Session state has been changed */ + EVENT_PEER_STATE_CHANGED /* FST related generic event occurred, + * see struct fst_hostap_event_data for + * more info */ +}; + +enum fst_initiator { + FST_INITIATOR_UNDEFINED, + FST_INITIATOR_LOCAL, + FST_INITIATOR_REMOTE, +}; + +union fst_event_extra { + struct fst_event_extra_iface_state { + Boolean attached; + char ifname[FST_MAX_INTERFACE_SIZE]; + char group_id[FST_MAX_GROUP_ID_SIZE]; + } iface_state; /* for EVENT_FST_IFACE_STATE_CHANGED */ + struct fst_event_extra_peer_state { + Boolean connected; + char ifname[FST_MAX_INTERFACE_SIZE]; + u8 addr[ETH_ALEN]; + } peer_state; /* for EVENT_PEER_STATE_CHANGED */ + struct fst_event_extra_session_state { + enum fst_session_state old_state; + 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; + u8 reject_code; /* REASON_REJECT */ + /* REASON_SWITCH, + * REASON_TEARDOWN, + * REASON_REJECT + */ + enum fst_initiator initiator; + } to_initial; + } extra; + } session_state; /* for EVENT_FST_SESSION_STATE_CHANGED */ +}; + +/* helpers - prints enum in string form */ +#define FST_NAME_UNKNOWN "UNKNOWN" + +const char * fst_get_str_name(unsigned index, const char *names[], + size_t names_size); + +const char * fst_session_event_type_name(enum fst_event_type); +const char * fst_reason_name(enum fst_reason reason); +const char * fst_session_state_name(enum fst_session_state state); + +#endif /* FST_CTRL_AUX_H */ diff --git a/src/fst/fst_ctrl_defs.h b/src/fst/fst_ctrl_defs.h new file mode 100644 index 0000000000000..67353890ffcf9 --- /dev/null +++ b/src/fst/fst_ctrl_defs.h @@ -0,0 +1,109 @@ +/* + * FST module - shared Control interface definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_CTRL_DEFS_H +#define FST_CTRL_DEFS_H + +/* Undefined value */ +#define FST_CTRL_PVAL_NONE "NONE" + +/* FST-ATTACH parameters */ +#define FST_ATTACH_CMD_PNAME_LLT "llt" /* pval = desired LLT */ +#define FST_ATTACH_CMD_PNAME_PRIORITY "priority" /* pval = desired priority */ + +/* FST-MANAGER parameters */ +/* FST Session states */ +#define FST_CS_PVAL_STATE_INITIAL "INITIAL" +#define FST_CS_PVAL_STATE_SETUP_COMPLETION "SETUP_COMPLETION" +#define FST_CS_PVAL_STATE_TRANSITION_DONE "TRANSITION_DONE" +#define FST_CS_PVAL_STATE_TRANSITION_CONFIRMED "TRANSITION_CONFIRMED" + +/* FST Session reset reasons */ +#define FST_CS_PVAL_REASON_TEARDOWN "REASON_TEARDOWN" +#define FST_CS_PVAL_REASON_SETUP "REASON_SETUP" +#define FST_CS_PVAL_REASON_SWITCH "REASON_SWITCH" +#define FST_CS_PVAL_REASON_STT "REASON_STT" +#define FST_CS_PVAL_REASON_REJECT "REASON_REJECT" +#define FST_CS_PVAL_REASON_ERROR_PARAMS "REASON_ERROR_PARAMS" +#define FST_CS_PVAL_REASON_RESET "REASON_RESET" +#define FST_CS_PVAL_REASON_DETACH_IFACE "REASON_DETACH_IFACE" + +/* FST Session responses */ +#define FST_CS_PVAL_RESPONSE_ACCEPT "ACCEPT" +#define FST_CS_PVAL_RESPONSE_REJECT "REJECT" + +/* FST Session action initiator */ +#define FST_CS_PVAL_INITIATOR_LOCAL "LOCAL" +#define FST_CS_PVAL_INITIATOR_REMOTE "REMOTE" + +/* FST-CLI subcommands and parameter names */ +#define FST_CMD_LIST_GROUPS "list_groups" +#define FST_CMD_LIST_IFACES "list_ifaces" +#define FST_CMD_IFACE_PEERS "iface_peers" +#define FST_CMD_GET_PEER_MBIES "get_peer_mbies" +#define FST_CMD_LIST_SESSIONS "list_sessions" +#define FST_CMD_SESSION_ADD "session_add" +#define FST_CMD_SESSION_REMOVE "session_remove" +#define FST_CMD_SESSION_GET "session_get" +#define FST_CSG_PNAME_OLD_PEER_ADDR "old_peer_addr" /* pval = address string */ +#define FST_CSG_PNAME_NEW_PEER_ADDR "new_peer_addr" /* pval = address string */ +#define FST_CSG_PNAME_OLD_IFNAME "old_ifname" /* pval = ifname */ +#define FST_CSG_PNAME_NEW_IFNAME "new_ifname" /* pval = ifname */ +#define FST_CSG_PNAME_LLT "llt" /* pval = numeric llt value */ +#define FST_CSG_PNAME_STATE "state" /* pval = FST_CS_PVAL_STATE_... */ +#define FST_CMD_SESSION_SET "session_set" +#define FST_CSS_PNAME_OLD_PEER_ADDR FST_CSG_PNAME_OLD_PEER_ADDR +#define FST_CSS_PNAME_NEW_PEER_ADDR FST_CSG_PNAME_NEW_PEER_ADDR +#define FST_CSS_PNAME_OLD_IFNAME FST_CSG_PNAME_OLD_IFNAME +#define FST_CSS_PNAME_NEW_IFNAME FST_CSG_PNAME_NEW_IFNAME +#define FST_CSS_PNAME_LLT FST_CSG_PNAME_LLT +#define FST_CMD_SESSION_INITIATE "session_initiate" +#define FST_CMD_SESSION_RESPOND "session_respond" +#define FST_CMD_SESSION_TRANSFER "session_transfer" +#define FST_CMD_SESSION_TEARDOWN "session_teardown" + +#ifdef CONFIG_FST_TEST +#define FST_CTR_PVAL_BAD_NEW_BAND "bad_new_band" + +#define FST_CMD_TEST_REQUEST "test_request" +#define FST_CTR_IS_SUPPORTED "is_supported" +#define FST_CTR_SEND_SETUP_REQUEST "send_setup_request" +#define FST_CTR_SEND_SETUP_RESPONSE "send_setup_response" +#define FST_CTR_SEND_ACK_REQUEST "send_ack_request" +#define FST_CTR_SEND_ACK_RESPONSE "send_ack_response" +#define FST_CTR_SEND_TEAR_DOWN "send_tear_down" +#define FST_CTR_GET_FSTS_ID "get_fsts_id" +#define FST_CTR_GET_LOCAL_MBIES "get_local_mbies" +#endif /* CONFIG_FST_TEST */ + +/* Events */ +#define FST_CTRL_EVENT_IFACE "FST-EVENT-IFACE" +#define FST_CEI_PNAME_IFNAME "ifname" +#define FST_CEI_PNAME_GROUP "group" +#define FST_CEI_PNAME_ATTACHED "attached" +#define FST_CEI_PNAME_DETACHED "detached" +#define FST_CTRL_EVENT_PEER "FST-EVENT-PEER" +#define FST_CEP_PNAME_IFNAME "ifname" +#define FST_CEP_PNAME_ADDR "peer_addr" +#define FST_CEP_PNAME_CONNECTED "connected" +#define FST_CEP_PNAME_DISCONNECTED "disconnected" +#define FST_CTRL_EVENT_SESSION "FST-EVENT-SESSION" +#define FST_CES_PNAME_SESSION_ID "session_id" +#define FST_CES_PNAME_EVT_TYPE "event_type" +#define FST_PVAL_EVT_TYPE_SESSION_STATE "EVENT_FST_SESSION_STATE" +/* old_state/new_state: pval = FST_CS_PVAL_STATE_... */ +#define FST_CES_PNAME_OLD_STATE "old_state" +#define FST_CES_PNAME_NEW_STATE "new_state" +#define FST_CES_PNAME_REASON "reason" /* pval = FST_CS_PVAL_REASON_... */ +#define FST_CES_PNAME_REJECT_CODE "reject_code" /* pval = u8 code */ +/* pval = FST_CS_PVAL_INITIATOR_... */ +#define FST_CES_PNAME_INITIATOR "initiator" +#define FST_PVAL_EVT_TYPE_ESTABLISHED "EVENT_FST_ESTABLISHED" +#define FST_PVAL_EVT_TYPE_SETUP "EVENT_FST_SETUP" + +#endif /* FST_CTRL_DEFS_H */ diff --git a/src/fst/fst_ctrl_iface.c b/src/fst/fst_ctrl_iface.c new file mode 100644 index 0000000000000..d0907188a389e --- /dev/null +++ b/src/fst/fst_ctrl_iface.c @@ -0,0 +1,948 @@ +/* + * FST module - Control Interface implementation + * Copyright (c) 2014, 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/defs.h" +#include "list.h" +#include "fst/fst.h" +#include "fst/fst_internal.h" +#include "fst_ctrl_defs.h" +#include "fst_ctrl_iface.h" + + +static struct fst_group * get_fst_group_by_id(const char *id) +{ + struct fst_group *g; + + foreach_fst_group(g) { + const char *group_id = fst_group_get_id(g); + + if (os_strncmp(group_id, id, os_strlen(group_id)) == 0) + return g; + } + + return NULL; +} + + +/* notifications */ +static Boolean format_session_state_extra(const union fst_event_extra *extra, + char *buffer, size_t size) +{ + int len; + char reject_str[32] = FST_CTRL_PVAL_NONE; + const char *initiator = FST_CTRL_PVAL_NONE; + const struct fst_event_extra_session_state *ss; + + ss = &extra->session_state; + if (ss->new_state != FST_SESSION_STATE_INITIAL) + return TRUE; + + switch (ss->extra.to_initial.reason) { + case REASON_REJECT: + 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 */ + case REASON_TEARDOWN: + case REASON_SWITCH: + switch (ss->extra.to_initial.initiator) { + case FST_INITIATOR_LOCAL: + initiator = FST_CS_PVAL_INITIATOR_LOCAL; + break; + case FST_INITIATOR_REMOTE: + initiator = FST_CS_PVAL_INITIATOR_REMOTE; + break; + default: + break; + } + break; + default: + break; + } + + len = os_snprintf(buffer, size, + FST_CES_PNAME_REASON "=%s " + FST_CES_PNAME_REJECT_CODE "=%s " + FST_CES_PNAME_INITIATOR "=%s", + fst_reason_name(ss->extra.to_initial.reason), + reject_str, initiator); + + return !os_snprintf_error(size, len); +} + + +static void fst_ctrl_iface_notify(struct fst_iface *f, u32 session_id, + enum fst_event_type event_type, + const union fst_event_extra *extra) +{ + struct fst_group *g; + char extra_str[128] = ""; + const struct fst_event_extra_session_state *ss; + const struct fst_event_extra_iface_state *is; + const struct fst_event_extra_peer_state *ps; + + /* + * FST can use any of interface objects as it only sends messages + * on global Control Interface, so we just pick the 1st one. + */ + + if (!f) { + foreach_fst_group(g) { + f = fst_group_first_iface(g); + if (f) + break; + } + if (!f) + return; + } + + WPA_ASSERT(f->iface_obj.ctx); + + switch (event_type) { + case EVENT_FST_IFACE_STATE_CHANGED: + if (!extra) + return; + is = &extra->iface_state; + wpa_msg_global_only(f->iface_obj.ctx, MSG_INFO, + FST_CTRL_EVENT_IFACE " %s " + FST_CEI_PNAME_IFNAME "=%s " + FST_CEI_PNAME_GROUP "=%s", + is->attached ? FST_CEI_PNAME_ATTACHED : + FST_CEI_PNAME_DETACHED, + is->ifname, is->group_id); + break; + case EVENT_PEER_STATE_CHANGED: + if (!extra) + return; + ps = &extra->peer_state; + wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO, + FST_CTRL_EVENT_PEER " %s " + FST_CEP_PNAME_IFNAME "=%s " + FST_CEP_PNAME_ADDR "=" MACSTR, + ps->connected ? FST_CEP_PNAME_CONNECTED : + FST_CEP_PNAME_DISCONNECTED, + ps->ifname, MAC2STR(ps->addr)); + break; + case EVENT_FST_SESSION_STATE_CHANGED: + if (!extra) + return; + if (!format_session_state_extra(extra, extra_str, + sizeof(extra_str))) { + fst_printf(MSG_ERROR, + "CTRL: Cannot format STATE_CHANGE extra"); + extra_str[0] = 0; + } + ss = &extra->session_state; + wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO, + FST_CTRL_EVENT_SESSION " " + FST_CES_PNAME_SESSION_ID "=%u " + FST_CES_PNAME_EVT_TYPE "=%s " + FST_CES_PNAME_OLD_STATE "=%s " + FST_CES_PNAME_NEW_STATE "=%s %s", + session_id, + fst_session_event_type_name(event_type), + fst_session_state_name(ss->old_state), + fst_session_state_name(ss->new_state), + extra_str); + break; + case EVENT_FST_ESTABLISHED: + case EVENT_FST_SETUP: + wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO, + FST_CTRL_EVENT_SESSION " " + FST_CES_PNAME_SESSION_ID "=%u " + FST_CES_PNAME_EVT_TYPE "=%s", + session_id, + fst_session_event_type_name(event_type)); + break; + } +} + + +/* command processors */ + +/* fst session_get */ +static int session_get(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + struct fst_iface *new_iface, *old_iface; + const u8 *old_peer_addr, *new_peer_addr; + u32 id; + + id = strtoul(session_id, NULL, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + old_peer_addr = fst_session_get_peer_addr(s, TRUE); + new_peer_addr = fst_session_get_peer_addr(s, FALSE); + new_iface = fst_session_get_iface(s, FALSE); + old_iface = fst_session_get_iface(s, TRUE); + + return os_snprintf(buf, buflen, + FST_CSG_PNAME_OLD_PEER_ADDR "=" MACSTR "\n" + FST_CSG_PNAME_NEW_PEER_ADDR "=" MACSTR "\n" + FST_CSG_PNAME_NEW_IFNAME "=%s\n" + FST_CSG_PNAME_OLD_IFNAME "=%s\n" + FST_CSG_PNAME_LLT "=%u\n" + FST_CSG_PNAME_STATE "=%s\n", + MAC2STR(old_peer_addr), + MAC2STR(new_peer_addr), + new_iface ? fst_iface_get_name(new_iface) : + FST_CTRL_PVAL_NONE, + old_iface ? fst_iface_get_name(old_iface) : + FST_CTRL_PVAL_NONE, + fst_session_get_llt(s), + fst_session_state_name(fst_session_get_state(s))); +} + + +/* fst session_set */ +static int session_set(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + char *p, *q; + u32 id; + int ret; + + id = strtoul(session_id, &p, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + if (*p != ' ' || !(q = os_strchr(p + 1, '='))) + return os_snprintf(buf, buflen, "FAIL\n"); + p++; + + if (os_strncasecmp(p, FST_CSS_PNAME_OLD_IFNAME, q - p) == 0) { + ret = fst_session_set_str_ifname(s, q + 1, TRUE); + } else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_IFNAME, q - p) == 0) { + ret = fst_session_set_str_ifname(s, q + 1, FALSE); + } else if (os_strncasecmp(p, FST_CSS_PNAME_OLD_PEER_ADDR, q - p) == 0) { + ret = fst_session_set_str_peer_addr(s, q + 1, TRUE); + } else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_PEER_ADDR, q - p) == 0) { + ret = fst_session_set_str_peer_addr(s, q + 1, FALSE); + } else if (os_strncasecmp(p, FST_CSS_PNAME_LLT, q - p) == 0) { + ret = fst_session_set_str_llt(s, q + 1); + } else { + fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK"); +} + + +/* fst session_add/remove */ +static int session_add(const char *group_id, char *buf, size_t buflen) +{ + struct fst_group *g; + struct fst_session *s; + + g = get_fst_group_by_id(group_id); + if (!g) { + fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'", + group_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + s = fst_session_create(g); + if (!s) { + fst_printf(MSG_ERROR, + "CTRL: Cannot create session for group '%s'", + group_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "%u\n", fst_session_get_id(s)); +} + + +static int session_remove(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + struct fst_group *g; + u32 id; + + id = strtoul(session_id, NULL, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + g = fst_session_get_group(s); + fst_session_reset(s); + fst_session_delete(s); + fst_group_delete_if_empty(g); + + return os_snprintf(buf, buflen, "OK\n"); +} + + +/* fst session_initiate */ +static int session_initiate(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + u32 id; + + id = strtoul(session_id, NULL, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + if (fst_session_initiate_setup(s)) { + fst_printf(MSG_WARNING, "CTRL: Cannot initiate session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "OK\n"); +} + + +/* fst session_respond */ +static int session_respond(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + char *p; + u32 id; + u8 status_code; + + id = strtoul(session_id, &p, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + if (*p != ' ') + return os_snprintf(buf, buflen, "FAIL\n"); + p++; + + if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_ACCEPT)) { + status_code = WLAN_STATUS_SUCCESS; + } else if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_REJECT)) { + status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION; + } else { + fst_printf(MSG_WARNING, + "CTRL: session %u: unknown response status: %s", + id, p); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + if (fst_session_respond(s, status_code)) { + fst_printf(MSG_WARNING, "CTRL: Cannot respond to session %u", + id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + fst_printf(MSG_INFO, "CTRL: session %u responded", id); + + return os_snprintf(buf, buflen, "OK\n"); +} + + +/* fst session_transfer */ +static int session_transfer(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + u32 id; + + id = strtoul(session_id, NULL, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + if (fst_session_initiate_switch(s)) { + fst_printf(MSG_WARNING, + "CTRL: Cannot initiate ST for session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "OK\n"); +} + + +/* fst session_teardown */ +static int session_teardown(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + u32 id; + + id = strtoul(session_id, NULL, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + if (fst_session_tear_down_setup(s)) { + fst_printf(MSG_WARNING, "CTRL: Cannot tear down session %u", + id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "OK\n"); +} + + +#ifdef CONFIG_FST_TEST +/* fst test_request */ +static int test_request(const char *request, char *buf, size_t buflen) +{ + const char *p = request; + int ret; + + if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_REQUEST, + os_strlen(FST_CTR_SEND_SETUP_REQUEST))) { + ret = fst_test_req_send_fst_request( + p + os_strlen(FST_CTR_SEND_SETUP_REQUEST)); + } else if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_RESPONSE, + os_strlen(FST_CTR_SEND_SETUP_RESPONSE))) { + ret = fst_test_req_send_fst_response( + p + os_strlen(FST_CTR_SEND_SETUP_RESPONSE)); + } else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_REQUEST, + os_strlen(FST_CTR_SEND_ACK_REQUEST))) { + ret = fst_test_req_send_ack_request( + p + os_strlen(FST_CTR_SEND_ACK_REQUEST)); + } else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_RESPONSE, + os_strlen(FST_CTR_SEND_ACK_RESPONSE))) { + ret = fst_test_req_send_ack_response( + p + os_strlen(FST_CTR_SEND_ACK_RESPONSE)); + } else if (!os_strncasecmp(p, FST_CTR_SEND_TEAR_DOWN, + os_strlen(FST_CTR_SEND_TEAR_DOWN))) { + ret = fst_test_req_send_tear_down( + p + os_strlen(FST_CTR_SEND_TEAR_DOWN)); + } else if (!os_strncasecmp(p, FST_CTR_GET_FSTS_ID, + os_strlen(FST_CTR_GET_FSTS_ID))) { + u32 fsts_id = fst_test_req_get_fsts_id( + p + os_strlen(FST_CTR_GET_FSTS_ID)); + if (fsts_id != FST_FSTS_ID_NOT_FOUND) + return os_snprintf(buf, buflen, "%u\n", fsts_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } else if (!os_strncasecmp(p, FST_CTR_GET_LOCAL_MBIES, + os_strlen(FST_CTR_GET_LOCAL_MBIES))) { + return fst_test_req_get_local_mbies( + p + os_strlen(FST_CTR_GET_LOCAL_MBIES), buf, buflen); + } else if (!os_strncasecmp(p, FST_CTR_IS_SUPPORTED, + os_strlen(FST_CTR_IS_SUPPORTED))) { + ret = 0; + } else { + fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK"); +} +#endif /* CONFIG_FST_TEST */ + + +/* fst list_sessions */ +struct list_sessions_cb_ctx { + char *buf; + size_t buflen; + size_t reply_len; +}; + + +static void list_session_enum_cb(struct fst_group *g, struct fst_session *s, + void *ctx) +{ + struct list_sessions_cb_ctx *c = ctx; + int ret; + + ret = os_snprintf(c->buf, c->buflen, " %u", fst_session_get_id(s)); + + c->buf += ret; + c->buflen -= ret; + c->reply_len += ret; +} + + +static int list_sessions(const char *group_id, char *buf, size_t buflen) +{ + struct list_sessions_cb_ctx ctx; + struct fst_group *g; + + g = get_fst_group_by_id(group_id); + if (!g) { + fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'", + group_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + ctx.buf = buf; + ctx.buflen = buflen; + ctx.reply_len = 0; + + fst_session_enum(g, list_session_enum_cb, &ctx); + + ctx.reply_len += os_snprintf(buf + ctx.reply_len, ctx.buflen, "\n"); + + return ctx.reply_len; +} + + +/* fst iface_peers */ +static int iface_peers(const char *group_id, char *buf, size_t buflen) +{ + const char *ifname; + struct fst_group *g; + struct fst_iface *f; + struct fst_get_peer_ctx *ctx; + const u8 *addr; + unsigned found = 0; + int ret = 0; + + g = get_fst_group_by_id(group_id); + if (!g) { + fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'", + group_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + ifname = os_strchr(group_id, ' '); + if (!ifname) + return os_snprintf(buf, buflen, "FAIL\n"); + ifname++; + + foreach_fst_group_iface(g, f) { + const char *in = fst_iface_get_name(f); + + if (os_strncmp(ifname, in, os_strlen(in)) == 0) { + found = 1; + break; + } + } + + if (!found) + return os_snprintf(buf, buflen, "FAIL\n"); + + addr = fst_iface_get_peer_first(f, &ctx, FALSE); + for (; addr != NULL; addr = fst_iface_get_peer_next(f, &ctx, FALSE)) { + int res; + + res = os_snprintf(buf + ret, buflen - ret, MACSTR "\n", + MAC2STR(addr)); + if (os_snprintf_error(buflen - ret, res)) + break; + ret += res; + } + + return ret; +} + + +static int get_peer_mbies(const char *params, char *buf, size_t buflen) +{ + char *endp; + char ifname[FST_MAX_INTERFACE_SIZE]; + u8 peer_addr[ETH_ALEN]; + struct fst_group *g; + struct fst_iface *iface = NULL; + const struct wpabuf *mbies; + + if (fst_read_next_text_param(params, ifname, sizeof(ifname), &endp) || + !*ifname) + goto problem; + + while (isspace(*endp)) + endp++; + if (fst_read_peer_addr(endp, peer_addr)) + goto problem; + + foreach_fst_group(g) { + iface = fst_group_get_iface_by_name(g, ifname); + if (iface) + break; + } + if (!iface) + goto problem; + + mbies = fst_iface_get_peer_mb_ie(iface, peer_addr); + if (!mbies) + goto problem; + + return wpa_snprintf_hex(buf, buflen, wpabuf_head(mbies), + wpabuf_len(mbies)); + +problem: + return os_snprintf(buf, buflen, "FAIL\n"); +} + + +/* fst list_ifaces */ +static int list_ifaces(const char *group_id, char *buf, size_t buflen) +{ + struct fst_group *g; + struct fst_iface *f; + int ret = 0; + + g = get_fst_group_by_id(group_id); + if (!g) { + fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'", + group_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + foreach_fst_group_iface(g, f) { + int res; + const u8 *iface_addr = fst_iface_get_addr(f); + + res = os_snprintf(buf + ret, buflen - ret, + "%s|" MACSTR "|%u|%u\n", + fst_iface_get_name(f), + MAC2STR(iface_addr), + fst_iface_get_priority(f), + fst_iface_get_llt(f)); + if (os_snprintf_error(buflen - ret, res)) + break; + ret += res; + } + + return ret; +} + + +/* fst list_groups */ +static int list_groups(const char *cmd, char *buf, size_t buflen) +{ + struct fst_group *g; + int ret = 0; + + foreach_fst_group(g) { + int res; + + res = os_snprintf(buf + ret, buflen - ret, "%s\n", + fst_group_get_id(g)); + if (os_snprintf_error(buflen - ret, res)) + break; + ret += res; + } + + return ret; +} + + +static const char * band_freq(enum mb_band_id band) +{ + static const char *band_names[] = { + [MB_BAND_ID_WIFI_2_4GHZ] "2.4GHZ", + [MB_BAND_ID_WIFI_5GHZ] "5GHZ", + [MB_BAND_ID_WIFI_60GHZ] "60GHZ", + }; + + return fst_get_str_name(band, band_names, ARRAY_SIZE(band_names)); +} + + +static int print_band(unsigned num, struct fst_iface *iface, const u8 *addr, + char *buf, size_t buflen) +{ + const struct wpabuf *wpabuf; + enum hostapd_hw_mode hw_mode; + u8 channel; + int ret = 0; + + fst_iface_get_channel_info(iface, &hw_mode, &channel); + + ret += os_snprintf(buf + ret, buflen - ret, "band%u_frequency=%s\n", + num, band_freq(fst_hw_mode_to_band(hw_mode))); + ret += os_snprintf(buf + ret, buflen - ret, "band%u_iface=%s\n", + num, fst_iface_get_name(iface)); + wpabuf = fst_iface_get_peer_mb_ie(iface, addr); + if (wpabuf) { + ret += os_snprintf(buf + ret, buflen - ret, "band%u_mb_ies=", + num); + ret += wpa_snprintf_hex(buf + ret, buflen - ret, + wpabuf_head(wpabuf), + wpabuf_len(wpabuf)); + ret += os_snprintf(buf + ret, buflen - ret, "\n"); + } + ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_group_id=%s\n", + num, fst_iface_get_group_id(iface)); + ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_priority=%u\n", + num, fst_iface_get_priority(iface)); + ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_llt=%u\n", + num, fst_iface_get_llt(iface)); + + return ret; +} + + +static void fst_ctrl_iface_on_iface_state_changed(struct fst_iface *i, + Boolean attached) +{ + union fst_event_extra extra; + + os_memset(&extra, 0, sizeof(extra)); + extra.iface_state.attached = attached; + os_strlcpy(extra.iface_state.ifname, fst_iface_get_name(i), + sizeof(extra.iface_state.ifname)); + os_strlcpy(extra.iface_state.group_id, fst_iface_get_group_id(i), + sizeof(extra.iface_state.group_id)); + + fst_ctrl_iface_notify(i, FST_INVALID_SESSION_ID, + EVENT_FST_IFACE_STATE_CHANGED, &extra); +} + + +static int fst_ctrl_iface_on_iface_added(struct fst_iface *i) +{ + fst_ctrl_iface_on_iface_state_changed(i, TRUE); + return 0; +} + + +static void fst_ctrl_iface_on_iface_removed(struct fst_iface *i) +{ + fst_ctrl_iface_on_iface_state_changed(i, FALSE); +} + + +static void fst_ctrl_iface_on_event(enum fst_event_type event_type, + struct fst_iface *i, struct fst_session *s, + const union fst_event_extra *extra) +{ + u32 session_id = s ? fst_session_get_id(s) : FST_INVALID_SESSION_ID; + + fst_ctrl_iface_notify(i, session_id, event_type, extra); +} + + +static const struct fst_ctrl ctrl_cli = { + .on_iface_added = fst_ctrl_iface_on_iface_added, + .on_iface_removed = fst_ctrl_iface_on_iface_removed, + .on_event = fst_ctrl_iface_on_event, +}; + +const struct fst_ctrl *fst_ctrl_cli = &ctrl_cli; + + +int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen) +{ + struct fst_group *g; + struct fst_iface *f; + unsigned num = 0; + int ret = 0; + + foreach_fst_group(g) { + foreach_fst_group_iface(g, f) { + if (fst_iface_is_connected(f, addr)) { + ret += print_band(num++, f, addr, + buf + ret, buflen - ret); + } + } + } + + return ret; +} + + +/* fst ctrl processor */ +int fst_ctrl_iface_receive(const char *cmd, char *reply, size_t reply_size) +{ + static const struct fst_command { + const char *name; + unsigned has_param; + int (*process)(const char *group_id, char *buf, size_t buflen); + } commands[] = { + { FST_CMD_LIST_GROUPS, 0, list_groups}, + { FST_CMD_LIST_IFACES, 1, list_ifaces}, + { FST_CMD_IFACE_PEERS, 1, iface_peers}, + { FST_CMD_GET_PEER_MBIES, 1, get_peer_mbies}, + { FST_CMD_LIST_SESSIONS, 1, list_sessions}, + { FST_CMD_SESSION_ADD, 1, session_add}, + { FST_CMD_SESSION_REMOVE, 1, session_remove}, + { FST_CMD_SESSION_GET, 1, session_get}, + { FST_CMD_SESSION_SET, 1, session_set}, + { FST_CMD_SESSION_INITIATE, 1, session_initiate}, + { FST_CMD_SESSION_RESPOND, 1, session_respond}, + { FST_CMD_SESSION_TRANSFER, 1, session_transfer}, + { FST_CMD_SESSION_TEARDOWN, 1, session_teardown}, +#ifdef CONFIG_FST_TEST + { FST_CMD_TEST_REQUEST, 1, test_request }, +#endif /* CONFIG_FST_TEST */ + { NULL, 0, NULL } + }; + const struct fst_command *c; + const char *p; + const char *temp; + Boolean non_spaces_found; + + for (c = commands; c->name; c++) { + if (os_strncasecmp(cmd, c->name, os_strlen(c->name)) != 0) + continue; + p = cmd + os_strlen(c->name); + if (c->has_param) { + if (!isspace(p[0])) + return os_snprintf(reply, reply_size, "FAIL\n"); + p++; + temp = p; + non_spaces_found = FALSE; + while (*temp) { + if (!isspace(*temp)) { + non_spaces_found = TRUE; + break; + } + temp++; + } + if (!non_spaces_found) + return os_snprintf(reply, reply_size, "FAIL\n"); + } + return c->process(p, reply, reply_size); + } + + return os_snprintf(reply, reply_size, "UNKNOWN FST COMMAND\n"); +} + + +int fst_read_next_int_param(const char *params, Boolean *valid, char **endp) +{ + int ret = -1; + const char *curp; + + *valid = FALSE; + *endp = (char *) params; + curp = params; + if (*curp) { + ret = (int) strtol(curp, endp, 0); + if (!**endp || isspace(**endp)) + *valid = TRUE; + } + + return ret; +} + + +int fst_read_next_text_param(const char *params, char *buf, size_t buflen, + char **endp) +{ + size_t max_chars_to_copy; + char *cur_dest; + + *endp = (char *) params; + while (isspace(**endp)) + (*endp)++; + if (!**endp || buflen <= 1) + return -EINVAL; + + max_chars_to_copy = buflen - 1; + /* We need 1 byte for the terminating zero */ + cur_dest = buf; + while (**endp && !isspace(**endp) && max_chars_to_copy > 0) { + *cur_dest = **endp; + (*endp)++; + cur_dest++; + max_chars_to_copy--; + } + *cur_dest = 0; + + return 0; +} + + +int fst_read_peer_addr(const char *mac, u8 *peer_addr) +{ + if (hwaddr_aton(mac, peer_addr)) { + fst_printf(MSG_WARNING, "Bad peer_mac %s: invalid addr string", + mac); + return -1; + } + + if (is_zero_ether_addr(peer_addr) || + is_multicast_ether_addr(peer_addr)) { + fst_printf(MSG_WARNING, "Bad peer_mac %s: not a unicast addr", + mac); + return -1; + } + + return 0; +} + + +int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size, + struct fst_iface_cfg *cfg) +{ + char *pos; + char *endp; + Boolean is_valid; + int val; + + if (fst_read_next_text_param(cmd, ifname, ifname_size, &endp) || + fst_read_next_text_param(endp, cfg->group_id, sizeof(cfg->group_id), + &endp)) + return -EINVAL; + + cfg->llt = FST_DEFAULT_LLT_CFG_VALUE; + cfg->priority = 0; + pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_LLT); + if (pos) { + pos += os_strlen(FST_ATTACH_CMD_PNAME_LLT); + if (*pos == '=') { + val = fst_read_next_int_param(pos + 1, &is_valid, + &endp); + if (is_valid) + cfg->llt = val; + } + } + pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_PRIORITY); + if (pos) { + pos += os_strlen(FST_ATTACH_CMD_PNAME_PRIORITY); + if (*pos == '=') { + val = fst_read_next_int_param(pos + 1, &is_valid, + &endp); + if (is_valid) + cfg->priority = (u8) val; + } + } + + return 0; +} + + +int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size) +{ + char *endp; + + return fst_read_next_text_param(cmd, ifname, ifname_size, &endp); +} + + +int fst_iface_detach(const char *ifname) +{ + struct fst_group *g; + + foreach_fst_group(g) { + struct fst_iface *f; + + f = fst_group_get_iface_by_name(g, ifname); + if (f) { + fst_detach(f); + return 0; + } + } + + return -EINVAL; +} diff --git a/src/fst/fst_ctrl_iface.h b/src/fst/fst_ctrl_iface.h new file mode 100644 index 0000000000000..4d0cd9fce4e00 --- /dev/null +++ b/src/fst/fst_ctrl_iface.h @@ -0,0 +1,45 @@ +/* + * FST module - internal Control interface definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_CTRL_IFACE_H +#define FST_CTRL_IFACE_H + +#include "fst/fst_ctrl_aux.h" + +#ifdef CONFIG_FST + +/* receiver */ +int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen); + +int fst_ctrl_iface_receive(const char *txtaddr, char *buf, size_t buflen); + +extern const struct fst_ctrl *fst_ctrl_cli; + +#else /* CONFIG_FST */ + +static inline int +fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen) +{ + return 0; +} + +#endif /* CONFIG_FST */ + +int fst_read_next_int_param(const char *params, Boolean *valid, char **endp); +int fst_read_next_text_param(const char *params, char *buf, size_t buflen, + char **endp); +int fst_read_peer_addr(const char *mac, u8 *peer_addr); + +struct fst_iface_cfg; + +int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size, + struct fst_iface_cfg *cfg); +int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size); +int fst_iface_detach(const char *ifname); + +#endif /* CTRL_IFACE_FST_H */ diff --git a/src/fst/fst_defs.h b/src/fst/fst_defs.h new file mode 100644 index 0000000000000..8ddcc61376b2d --- /dev/null +++ b/src/fst/fst_defs.h @@ -0,0 +1,87 @@ +/* + * FST module - FST related definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IEEE_80211_FST_DEFS_H +#define IEEE_80211_FST_DEFS_H + +/* IEEE Std 802.11ad */ + +#define MB_STA_CHANNEL_ALL 0 + +enum session_type { + SESSION_TYPE_BSS = 0, /* Infrastructure BSS */ + SESSION_TYPE_IBSS = 1, + SESSION_TYPE_DLS = 2, + SESSION_TYPE_TDLS = 3, + SESSION_TYPE_PBSS = 4 +}; + +#define SESSION_CONTROL(session_type, switch_intent) \ + (((u8) ((session_type) & 0x7)) | ((switch_intent) ? 0x10 : 0x00)) + +#define GET_SESSION_CONTROL_TYPE(session_control) \ + ((u8) ((session_control) & 0x7)) + +#define GET_SESSION_CONTROL_SWITCH_INTENT(session_control) \ + (((session_control) & 0x10) >> 4) + +/* 8.4.2.147 Session Transition element */ +struct session_transition_ie { + u8 element_id; + u8 length; + u32 fsts_id; + u8 session_control; + u8 new_band_id; + u8 new_band_setup; + u8 new_band_op; + u8 old_band_id; + u8 old_band_setup; + u8 old_band_op; +} STRUCT_PACKED; + +struct fst_setup_req { + u8 action; + u8 dialog_token; + u32 llt; + struct session_transition_ie stie; + /* Multi-band (optional) */ + /* Wakeup Schedule (optional) */ + /* Awake Window (optional) */ + /* Switching Stream (optional) */ +} STRUCT_PACKED; + +struct fst_setup_res { + u8 action; + u8 dialog_token; + u8 status_code; + struct session_transition_ie stie; + /* Multi-band (optional) */ + /* Wakeup Schedule (optional) */ + /* Awake Window (optional) */ + /* Switching Stream (optional) */ + /* Timeout Interval (optional) */ +} STRUCT_PACKED; + +struct fst_ack_req { + u8 action; + u8 dialog_token; + u32 fsts_id; +} STRUCT_PACKED; + +struct fst_ack_res { + u8 action; + u8 dialog_token; + u32 fsts_id; +} STRUCT_PACKED; + +struct fst_tear_down { + u8 action; + u32 fsts_id; +} STRUCT_PACKED; + +#endif /* IEEE_80211_FST_DEFS_H */ diff --git a/src/fst/fst_group.c b/src/fst/fst_group.c new file mode 100644 index 0000000000000..f6c7be9435f41 --- /dev/null +++ b/src/fst/fst_group.c @@ -0,0 +1,462 @@ +/* + * FST module - FST group object implementation + * Copyright (c) 2014, 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/defs.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "drivers/driver.h" +#include "fst/fst_internal.h" +#include "fst/fst_defs.h" + + +struct dl_list fst_global_groups_list; + +#ifndef HOSTAPD +static Boolean fst_has_fst_peer(struct fst_iface *iface, Boolean *has_peer) +{ + const u8 *bssid; + + bssid = fst_iface_get_bssid(iface); + if (!bssid) { + *has_peer = FALSE; + return FALSE; + } + + *has_peer = TRUE; + return fst_iface_get_peer_mb_ie(iface, bssid) != NULL; +} +#endif /* HOSTAPD */ + + +static void fst_dump_mb_ies(const char *group_id, const char *ifname, + struct wpabuf *mbies) +{ + const u8 *p = wpabuf_head(mbies); + size_t s = wpabuf_len(mbies); + + while (s >= 2) { + 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)); + + fst_printf(MSG_WARNING, + "%s: %s: mb_ctrl=%u band_id=%u op_class=%u chan=%u bssid=" + MACSTR + " beacon_int=%u tsf_offs=[%u %u %u %u %u %u %u %u] mb_cc=0x%02x tmout=%u", + group_id, ifname, + mbie->mb_ctrl, mbie->band_id, mbie->op_class, + mbie->chan, MAC2STR(mbie->bssid), mbie->beacon_int, + mbie->tsf_offs[0], mbie->tsf_offs[1], + mbie->tsf_offs[2], mbie->tsf_offs[3], + mbie->tsf_offs[4], mbie->tsf_offs[5], + mbie->tsf_offs[6], mbie->tsf_offs[7], + mbie->mb_connection_capability, + mbie->fst_session_tmout); + + p += 2 + mbie->len; + s -= 2 + mbie->len; + } +} + + +static void fst_fill_mb_ie(struct wpabuf *buf, const u8 *bssid, + const u8 *own_addr, enum mb_band_id band, u8 channel) +{ + struct multi_band_ie *mbie; + size_t len = sizeof(*mbie); + + if (own_addr) + len += ETH_ALEN; + + mbie = wpabuf_put(buf, len); + + os_memset(mbie, 0, len); + + mbie->eid = WLAN_EID_MULTI_BAND; + mbie->len = len - 2; +#ifdef HOSTAPD + mbie->mb_ctrl = MB_STA_ROLE_AP; + mbie->mb_connection_capability = MB_CONNECTION_CAPABILITY_AP; +#else /* HOSTAPD */ + mbie->mb_ctrl = MB_STA_ROLE_NON_PCP_NON_AP; + mbie->mb_connection_capability = 0; +#endif /* HOSTAPD */ + if (bssid) + os_memcpy(mbie->bssid, bssid, ETH_ALEN); + mbie->band_id = band; + mbie->op_class = 0; /* means all */ + mbie->chan = channel; + mbie->fst_session_tmout = FST_DEFAULT_SESSION_TIMEOUT_TU; + + if (own_addr) { + mbie->mb_ctrl |= MB_CTRL_STA_MAC_PRESENT; + os_memcpy(&mbie[1], own_addr, ETH_ALEN); + } +} + + +static unsigned fst_fill_iface_mb_ies(struct fst_iface *f, struct wpabuf *buf) +{ + const u8 *bssid; + + bssid = fst_iface_get_bssid(f); + if (bssid) { + enum hostapd_hw_mode hw_mode; + u8 channel; + + if (buf) { + fst_iface_get_channel_info(f, &hw_mode, &channel); + fst_fill_mb_ie(buf, bssid, fst_iface_get_addr(f), + fst_hw_mode_to_band(hw_mode), channel); + } + return 1; + } else { + unsigned bands[MB_BAND_ID_WIFI_60GHZ + 1] = {}; + struct hostapd_hw_modes *modes; + enum mb_band_id b; + int num_modes = fst_iface_get_hw_modes(f, &modes); + int ret = 0; + + while (num_modes--) { + b = fst_hw_mode_to_band(modes->mode); + modes++; + if (b >= ARRAY_SIZE(bands) || bands[b]++) + continue; + ret++; + if (buf) + fst_fill_mb_ie(buf, NULL, fst_iface_get_addr(f), + b, MB_STA_CHANNEL_ALL); + } + return ret; + } +} + + +static struct wpabuf * fst_group_create_mb_ie(struct fst_group *g, + struct fst_iface *i) +{ + struct wpabuf *buf; + struct fst_iface *f; + unsigned int nof_mbies = 0; + unsigned int nof_ifaces_added = 0; +#ifndef HOSTAPD + Boolean has_peer; + Boolean has_fst_peer; + + foreach_fst_group_iface(g, f) { + has_fst_peer = fst_has_fst_peer(f, &has_peer); + if (has_peer && !has_fst_peer) + return NULL; + } +#endif /* HOSTAPD */ + + foreach_fst_group_iface(g, f) { + if (f == i) + continue; + nof_mbies += fst_fill_iface_mb_ies(f, NULL); + } + + buf = wpabuf_alloc(nof_mbies * + (sizeof(struct multi_band_ie) + ETH_ALEN)); + if (!buf) { + fst_printf_iface(i, MSG_ERROR, + "cannot allocate mem for %u MB IEs", + nof_mbies); + return NULL; + } + + /* The list is sorted in descending order by priorities, so MB IEs will + * be arranged in the same order, as required by spec (see corresponding + * comment in.fst_attach(). + */ + foreach_fst_group_iface(g, f) { + if (f == i) + continue; + + fst_fill_iface_mb_ies(f, buf); + ++nof_ifaces_added; + + fst_printf_iface(i, MSG_DEBUG, "added to MB IE"); + } + + if (!nof_ifaces_added) { + wpabuf_free(buf); + buf = NULL; + fst_printf_iface(i, MSG_INFO, + "cannot add MB IE: no backup ifaces"); + } else { + fst_dump_mb_ies(fst_group_get_id(g), fst_iface_get_name(i), + buf); + } + + return buf; +} + + +static const u8 * fst_mbie_get_peer_addr(const struct multi_band_ie *mbie) +{ + const u8 *peer_addr = NULL; + + switch (MB_CTRL_ROLE(mbie->mb_ctrl)) { + case MB_STA_ROLE_AP: + peer_addr = mbie->bssid; + break; + case MB_STA_ROLE_NON_PCP_NON_AP: + if (mbie->mb_ctrl & MB_CTRL_STA_MAC_PRESENT && + (size_t) 2 + mbie->len >= sizeof(*mbie) + ETH_ALEN) + peer_addr = (const u8 *) &mbie[1]; + break; + default: + break; + } + + return peer_addr; +} + + +static struct fst_iface * +fst_group_get_new_iface_by_mbie_and_band_id(struct fst_group *g, + const u8 *mb_ies_buff, + size_t mb_ies_size, + u8 band_id, + u8 *iface_peer_addr) +{ + while (mb_ies_size >= 2) { + const struct multi_band_ie *mbie = + (const struct multi_band_ie *) mb_ies_buff; + + if (mbie->eid != WLAN_EID_MULTI_BAND || + (size_t) 2 + mbie->len < sizeof(*mbie)) + break; + + if (mbie->band_id == band_id) { + struct fst_iface *iface; + + foreach_fst_group_iface(g, iface) { + const u8 *peer_addr = + fst_mbie_get_peer_addr(mbie); + + if (peer_addr && + fst_iface_is_connected(iface, peer_addr) && + band_id == fst_iface_get_band_id(iface)) { + os_memcpy(iface_peer_addr, peer_addr, + ETH_ALEN); + return iface; + } + } + break; + } + + mb_ies_buff += 2 + mbie->len; + mb_ies_size -= 2 + mbie->len; + } + + return NULL; +} + + +struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g, + const char *ifname) +{ + struct fst_iface *f; + + foreach_fst_group_iface(g, f) { + const char *in = fst_iface_get_name(f); + + if (os_strncmp(in, ifname, os_strlen(in)) == 0) + return f; + } + + return NULL; +} + + +u8 fst_group_assign_dialog_token(struct fst_group *g) +{ + g->dialog_token++; + if (g->dialog_token == 0) + g->dialog_token++; + return g->dialog_token; +} + + +u32 fst_group_assign_fsts_id(struct fst_group *g) +{ + g->fsts_id++; + return g->fsts_id; +} + + +static Boolean +fst_group_does_iface_appear_in_other_mbies(struct fst_group *g, + struct fst_iface *iface, + struct fst_iface *other, + u8 *peer_addr) +{ + struct fst_get_peer_ctx *ctx; + const u8 *addr; + const u8 *iface_addr; + enum mb_band_id iface_band_id; + + WPA_ASSERT(g == fst_iface_get_group(iface)); + WPA_ASSERT(g == fst_iface_get_group(other)); + + iface_addr = fst_iface_get_addr(iface); + iface_band_id = fst_iface_get_band_id(iface); + + addr = fst_iface_get_peer_first(other, &ctx, TRUE); + for (; addr; addr = fst_iface_get_peer_next(other, &ctx, TRUE)) { + const struct wpabuf *mbies; + u8 other_iface_peer_addr[ETH_ALEN]; + struct fst_iface *other_new_iface; + + mbies = fst_iface_get_peer_mb_ie(other, addr); + if (!mbies) + continue; + + other_new_iface = fst_group_get_new_iface_by_mbie_and_band_id( + g, wpabuf_head(mbies), wpabuf_len(mbies), + iface_band_id, other_iface_peer_addr); + if (other_new_iface == iface && + os_memcmp(iface_addr, other_iface_peer_addr, + ETH_ALEN) != 0) { + os_memcpy(peer_addr, addr, ETH_ALEN); + return TRUE; + } + } + + return FALSE; +} + + +struct fst_iface * +fst_group_find_new_iface_by_stie(struct fst_group *g, + struct fst_iface *iface, + const u8 *peer_addr, + const struct session_transition_ie *stie, + u8 *iface_peer_addr) +{ + struct fst_iface *i; + + foreach_fst_group_iface(g, i) { + if (i == iface || + stie->new_band_id != fst_iface_get_band_id(i)) + continue; + if (fst_group_does_iface_appear_in_other_mbies(g, iface, i, + iface_peer_addr)) + return i; + break; + } + return NULL; +} + + +struct fst_iface * +fst_group_get_new_iface_by_stie_and_mbie( + struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size, + const struct session_transition_ie *stie, u8 *iface_peer_addr) +{ + return fst_group_get_new_iface_by_mbie_and_band_id( + g, mb_ies_buff, mb_ies_size, stie->new_band_id, + iface_peer_addr); +} + + +struct fst_group * fst_group_create(const char *group_id) +{ + struct fst_group *g; + + g = os_zalloc(sizeof(*g)); + if (g == NULL) { + fst_printf(MSG_ERROR, "%s: Cannot alloc group", group_id); + return NULL; + } + + dl_list_init(&g->ifaces); + os_strlcpy(g->group_id, group_id, sizeof(g->group_id)); + + dl_list_add_tail(&fst_global_groups_list, &g->global_groups_lentry); + fst_printf_group(g, MSG_DEBUG, "instance created"); + + foreach_fst_ctrl_call(on_group_created, g); + + return g; +} + + +void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i) +{ + struct dl_list *list = &g->ifaces; + struct fst_iface *f; + + /* + * Add new interface to the list. + * The list is sorted in descending order by priority to allow + * multiple MB IEs creation according to the spec (see 10.32 Multi-band + * operation, 10.32.1 General), as they should be ordered according to + * priorities. + */ + foreach_fst_group_iface(g, f) { + if (fst_iface_get_priority(f) < fst_iface_get_priority(i)) + break; + list = &f->group_lentry; + } + dl_list_add(list, &i->group_lentry); +} + + +void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i) +{ + dl_list_del(&i->group_lentry); +} + + +void fst_group_delete(struct fst_group *group) +{ + struct fst_session *s; + + dl_list_del(&group->global_groups_lentry); + WPA_ASSERT(dl_list_empty(&group->ifaces)); + foreach_fst_ctrl_call(on_group_deleted, group); + fst_printf_group(group, MSG_DEBUG, "instance deleted"); + while ((s = fst_session_global_get_first_by_group(group)) != NULL) + fst_session_delete(s); + os_free(group); +} + + +Boolean fst_group_delete_if_empty(struct fst_group *group) +{ + Boolean is_empty = !fst_group_has_ifaces(group) && + !fst_session_global_get_first_by_group(group); + + if (is_empty) + fst_group_delete(group); + + return is_empty; +} + + +void fst_group_update_ie(struct fst_group *g) +{ + struct fst_iface *i; + + foreach_fst_group_iface(g, i) { + struct wpabuf *mbie = fst_group_create_mb_ie(g, i); + + if (!mbie) + fst_printf_iface(i, MSG_WARNING, "cannot create MB IE"); + + fst_iface_attach_mbie(i, mbie); + fst_iface_set_ies(i, mbie); + fst_printf_iface(i, MSG_DEBUG, "multi-band IE set to %p", mbie); + } +} diff --git a/src/fst/fst_group.h b/src/fst/fst_group.h new file mode 100644 index 0000000000000..3a87c0bc91c91 --- /dev/null +++ b/src/fst/fst_group.h @@ -0,0 +1,75 @@ +/* + * FST module - FST group object definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_GROUP_H +#define FST_GROUP_H + +struct fst_group { + char group_id[IFNAMSIZ + 1]; + struct dl_list ifaces; + u8 dialog_token; + u32 fsts_id; + struct dl_list global_groups_lentry; +}; + +struct session_transition_ie; + +#define foreach_fst_group_iface(g, i) \ + dl_list_for_each((i), &(g)->ifaces, struct fst_iface, group_lentry) + +struct fst_group * fst_group_create(const char *group_id); +void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i); +void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i); +void fst_group_delete(struct fst_group *g); + +void fst_group_update_ie(struct fst_group *g); + +static inline Boolean fst_group_has_ifaces(struct fst_group *g) +{ + return !dl_list_empty(&g->ifaces); +} + +static inline struct fst_iface * fst_group_first_iface(struct fst_group *g) +{ + return dl_list_first(&g->ifaces, struct fst_iface, group_lentry); +} + +static inline const char * fst_group_get_id(struct fst_group *g) +{ + return g->group_id; +} + +Boolean fst_group_delete_if_empty(struct fst_group *group); +struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g, + const char *ifname); +struct fst_iface * +fst_group_find_new_iface_by_stie(struct fst_group *g, + struct fst_iface *iface, + const u8 *peer_addr, + const struct session_transition_ie *stie, + u8 *iface_peer_addr); +struct fst_iface * +fst_group_get_new_iface_by_stie_and_mbie( + struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size, + const struct session_transition_ie *stie, u8 *iface_peer_addr); +u8 fst_group_assign_dialog_token(struct fst_group *g); +u32 fst_group_assign_fsts_id(struct fst_group *g); + +extern struct dl_list fst_global_groups_list; + +#define foreach_fst_group(g) \ + dl_list_for_each((g), &fst_global_groups_list, \ + struct fst_group, global_groups_lentry) + +static inline struct fst_group * fst_first_group(void) +{ + return dl_list_first(&fst_global_groups_list, struct fst_group, + global_groups_lentry); +} + +#endif /* FST_GROUP_H */ diff --git a/src/fst/fst_iface.c b/src/fst/fst_iface.c new file mode 100644 index 0000000000000..5a92d2c33e420 --- /dev/null +++ b/src/fst/fst_iface.c @@ -0,0 +1,79 @@ +/* + * FST module - FST interface object implementation + * Copyright (c) 2014, 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 "fst/fst_internal.h" +#include "fst/fst_defs.h" + + +struct fst_iface * fst_iface_create(struct fst_group *g, const char *ifname, + const u8 *own_addr, + const struct fst_wpa_obj *iface_obj, + const struct fst_iface_cfg *cfg) +{ + struct fst_iface *i; + + i = os_zalloc(sizeof(*i)); + if (!i) { + fst_printf_group(g, MSG_ERROR, "cannot allocate iface for %s", + ifname); + return NULL; + } + + i->cfg = *cfg; + i->iface_obj = *iface_obj; + i->group = g; + os_strlcpy(i->ifname, ifname, sizeof(i->ifname)); + os_memcpy(i->own_addr, own_addr, sizeof(i->own_addr)); + + if (!i->cfg.llt) { + fst_printf_iface(i, MSG_WARNING, "Zero llt adjusted"); + i->cfg.llt = FST_DEFAULT_LLT_CFG_VALUE; + } + + return i; +} + + +void fst_iface_delete(struct fst_iface *i) +{ + fst_iface_set_ies(i, NULL); + wpabuf_free(i->mb_ie); + os_free(i); +} + + +Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr) +{ + struct fst_get_peer_ctx *ctx; + const u8 *a = fst_iface_get_peer_first(iface, &ctx, TRUE); + + for (; a != NULL; a = fst_iface_get_peer_next(iface, &ctx, TRUE)) + if (os_memcmp(addr, a, ETH_ALEN) == 0) + return TRUE; + + return FALSE; +} + + +void fst_iface_attach_mbie(struct fst_iface *i, struct wpabuf *mbie) +{ + wpabuf_free(i->mb_ie); + i->mb_ie = mbie; +} + + +enum mb_band_id fst_iface_get_band_id(struct fst_iface *i) +{ + enum hostapd_hw_mode hw_mode; + u8 channel; + + fst_iface_get_channel_info(i, &hw_mode, &channel); + return fst_hw_mode_to_band(hw_mode); +} diff --git a/src/fst/fst_iface.h b/src/fst/fst_iface.h new file mode 100644 index 0000000000000..4670d894f7cbd --- /dev/null +++ b/src/fst/fst_iface.h @@ -0,0 +1,135 @@ +/* + * FST module - FST interface object definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + + +#ifndef FST_IFACE_H +#define FST_IFACE_H + +#include "utils/includes.h" +#include "utils/common.h" +#include "list.h" +#include "fst.h" + +struct fst_iface { + struct fst_group *group; + struct fst_wpa_obj iface_obj; + u8 own_addr[ETH_ALEN]; + struct wpabuf *mb_ie; + char ifname[IFNAMSIZ + 1]; + struct fst_iface_cfg cfg; + struct dl_list group_lentry; +}; + +struct fst_iface * fst_iface_create(struct fst_group *g, const char *ifname, + const u8 *own_addr, + const struct fst_wpa_obj *iface_obj, + const struct fst_iface_cfg *cfg); +void fst_iface_delete(struct fst_iface *i); + +static inline struct fst_group * fst_iface_get_group(struct fst_iface *i) +{ + return i->group; +} + +static inline const char * fst_iface_get_name(struct fst_iface *i) +{ + return i->ifname; +} + +static inline const u8 * fst_iface_get_addr(struct fst_iface *i) +{ + return i->own_addr; +} + +static inline const char * fst_iface_get_group_id(struct fst_iface *i) +{ + return i->cfg.group_id; +} + +static inline u8 fst_iface_get_priority(struct fst_iface *i) +{ + return i->cfg.priority; +} + +static inline u32 fst_iface_get_llt(struct fst_iface *i) +{ + return i->cfg.llt; +} + +static inline const struct wpabuf * fst_iface_get_mbie(struct fst_iface *i) +{ + return i->mb_ie; +} + +static inline const u8 * fst_iface_get_bssid(struct fst_iface *i) +{ + return i->iface_obj.get_bssid(i->iface_obj.ctx); +} + +static inline void fst_iface_get_channel_info(struct fst_iface *i, + enum hostapd_hw_mode *hw_mode, + u8 *channel) +{ + i->iface_obj.get_channel_info(i->iface_obj.ctx, hw_mode, channel); +} + +static inline int fst_iface_get_hw_modes(struct fst_iface *i, + struct hostapd_hw_modes **modes) +{ + return i->iface_obj.get_hw_modes(i->iface_obj.ctx, modes); +} + +static inline void fst_iface_set_ies(struct fst_iface *i, + const struct wpabuf *fst_ies) +{ + i->iface_obj.set_ies(i->iface_obj.ctx, fst_ies); +} + +static inline int fst_iface_send_action(struct fst_iface *i, + const u8 *addr, struct wpabuf *data) +{ + return i->iface_obj.send_action(i->iface_obj.ctx, addr, data); +} + +static inline const struct wpabuf * +fst_iface_get_peer_mb_ie(struct fst_iface *i, const u8 *addr) +{ + return i->iface_obj.get_mb_ie(i->iface_obj.ctx, addr); +} + +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); +} + +static inline const u8 * fst_iface_get_peer_first(struct fst_iface *i, + struct fst_get_peer_ctx **ctx, + Boolean mb_only) +{ + return i->iface_obj.get_peer_first(i->iface_obj.ctx, ctx, mb_only); +} + +static inline const u8 * fst_iface_get_peer_next(struct fst_iface *i, + struct fst_get_peer_ctx **ctx, + Boolean mb_only) +{ + return i->iface_obj.get_peer_next(i->iface_obj.ctx, ctx, mb_only); +} + +Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr); +void fst_iface_attach_mbie(struct fst_iface *i, struct wpabuf *mbie); +enum mb_band_id fst_iface_get_band_id(struct fst_iface *i); + +static inline void * fst_iface_get_wpa_obj_ctx(struct fst_iface *i) +{ + return i->iface_obj.ctx; +} + +#endif /* FST_IFACE_H */ diff --git a/src/fst/fst_internal.h b/src/fst/fst_internal.h new file mode 100644 index 0000000000000..9fe32b85409b8 --- /dev/null +++ b/src/fst/fst_internal.h @@ -0,0 +1,49 @@ +/* + * FST module - auxiliary definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_INTERNAL_H +#define FST_INTERNAL_H + +#include "utils/includes.h" +#include "utils/common.h" +#include "common/defs.h" +#include "common/ieee802_11_defs.h" +#include "fst/fst_iface.h" +#include "fst/fst_group.h" +#include "fst/fst_session.h" + +#define fst_printf(level, format, ...) \ + wpa_printf((level), "FST: " format, ##__VA_ARGS__) + +#define fst_printf_group(group, level, format, ...) \ + wpa_printf((level), "FST: %s: " format, \ + fst_group_get_id(group), ##__VA_ARGS__) + +#define fst_printf_iface(iface, level, format, ...) \ + fst_printf_group(fst_iface_get_group(iface), (level), "%s: " format, \ + fst_iface_get_name(iface), ##__VA_ARGS__) + +enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode); + +struct fst_ctrl_handle { + struct fst_ctrl ctrl; + struct dl_list global_ctrls_lentry; +}; + +extern struct dl_list fst_global_ctrls_list; + +#define foreach_fst_ctrl_call(clb, ...) \ + do { \ + struct fst_ctrl_handle *__fst_ctrl_h; \ + dl_list_for_each(__fst_ctrl_h, &fst_global_ctrls_list, \ + struct fst_ctrl_handle, global_ctrls_lentry) \ + if (__fst_ctrl_h->ctrl.clb) \ + __fst_ctrl_h->ctrl.clb(__VA_ARGS__);\ + } while (0) + +#endif /* FST_INTERNAL_H */ diff --git a/src/fst/fst_session.c b/src/fst/fst_session.c new file mode 100644 index 0000000000000..55fa69495e99a --- /dev/null +++ b/src/fst/fst_session.c @@ -0,0 +1,1620 @@ +/* + * FST module - FST Session implementation + * Copyright (c) 2014, 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/defs.h" +#include "fst/fst_internal.h" +#include "fst/fst_defs.h" +#include "fst/fst_ctrl_iface.h" +#ifdef CONFIG_FST_TEST +#include "fst/fst_ctrl_defs.h" +#endif /* CONFIG_FST_TEST */ + +#define US_80211_TU 1024 + +#define US_TO_TU(m) ((m) * / US_80211_TU) +#define TU_TO_US(m) ((m) * US_80211_TU) + +#define FST_LLT_SWITCH_IMMEDIATELY 0 + +#define fst_printf_session(s, level, format, ...) \ + fst_printf((level), "%u (0x%08x): [" MACSTR "," MACSTR "] :" format, \ + (s)->id, (s)->data.fsts_id, \ + MAC2STR((s)->data.old_peer_addr), \ + MAC2STR((s)->data.new_peer_addr), \ + ##__VA_ARGS__) + +#define fst_printf_siface(s, iface, level, format, ...) \ + fst_printf_session((s), (level), "%s: " format, \ + fst_iface_get_name(iface), ##__VA_ARGS__) + +#define fst_printf_sframe(s, is_old, level, format, ...) \ + fst_printf_siface((s), \ + (is_old) ? (s)->data.old_iface : (s)->data.new_iface, \ + (level), format, ##__VA_ARGS__) + +#define FST_LLT_MS_DEFAULT 50 +#define FST_ACTION_MAX_SUPPORTED FST_ACTION_ON_CHANNEL_TUNNEL + +const char * const fst_action_names[] = { + [FST_ACTION_SETUP_REQUEST] = "Setup Request", + [FST_ACTION_SETUP_RESPONSE] = "Setup Response", + [FST_ACTION_TEAR_DOWN] = "Tear Down", + [FST_ACTION_ACK_REQUEST] = "Ack Request", + [FST_ACTION_ACK_RESPONSE] = "Ack Response", + [FST_ACTION_ON_CHANNEL_TUNNEL] = "On Channel Tunnel", +}; + +struct fst_session { + struct { + /* Session configuration that can be zeroed on reset */ + u8 old_peer_addr[ETH_ALEN]; + u8 new_peer_addr[ETH_ALEN]; + struct fst_iface *new_iface; + struct fst_iface *old_iface; + u32 llt_ms; + u8 pending_setup_req_dlgt; + u32 fsts_id; /* FSTS ID, see spec, 8.4.2.147 + * Session Transition element */ + } data; + /* Session object internal fields which won't be zeroed on reset */ + struct dl_list global_sessions_lentry; + u32 id; /* Session object ID used to identify + * specific session object */ + struct fst_group *group; + enum fst_session_state state; + Boolean stt_armed; +}; + +static struct dl_list global_sessions_list; +static u32 global_session_id = 0; + +#define foreach_fst_session(s) \ + dl_list_for_each((s), &global_sessions_list, \ + struct fst_session, global_sessions_lentry) + +#define foreach_fst_session_safe(s, temp) \ + dl_list_for_each_safe((s), (temp), &global_sessions_list, \ + struct fst_session, global_sessions_lentry) + + +static void fst_session_global_inc_id(void) +{ + global_session_id++; + if (global_session_id == FST_INVALID_SESSION_ID) + global_session_id++; +} + + +int fst_session_global_init(void) +{ + dl_list_init(&global_sessions_list); + return 0; +} + + +void fst_session_global_deinit(void) +{ + WPA_ASSERT(dl_list_empty(&global_sessions_list)); +} + + +static inline void fst_session_notify_ctrl(struct fst_session *s, + enum fst_event_type event_type, + union fst_event_extra *extra) +{ + foreach_fst_ctrl_call(on_event, event_type, NULL, s, extra); +} + + +static void fst_session_set_state(struct fst_session *s, + enum fst_session_state state, + union fst_session_state_switch_extra *extra) +{ + if (s->state != state) { + union fst_event_extra evext = { + .session_state = { + .old_state = s->state, + .new_state = state, + }, + }; + + if (extra) + evext.session_state.extra = *extra; + fst_session_notify_ctrl(s, EVENT_FST_SESSION_STATE_CHANGED, + &evext); + fst_printf_session(s, MSG_INFO, "State: %s => %s", + fst_session_state_name(s->state), + fst_session_state_name(state)); + s->state = state; + } +} + + +static u32 fst_find_free_session_id(void) +{ + u32 i, id = FST_INVALID_SESSION_ID; + struct fst_session *s; + + for (i = 0; i < (u32) -1; i++) { + Boolean in_use = FALSE; + + foreach_fst_session(s) { + if (s->id == global_session_id) { + fst_session_global_inc_id(); + in_use = TRUE; + break; + } + } + if (!in_use) { + id = global_session_id; + fst_session_global_inc_id(); + break; + } + } + + return id; +} + + +static void fst_session_timeout_handler(void *eloop_data, void *user_ctx) +{ + struct fst_session *s = user_ctx; + union fst_session_state_switch_extra extra = { + .to_initial = { + .reason = REASON_STT, + }, + }; + + fst_printf_session(s, MSG_WARNING, "Session State Timeout"); + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &extra); +} + + +static void fst_session_stt_arm(struct fst_session *s) +{ + eloop_register_timeout(0, TU_TO_US(FST_DEFAULT_SESSION_TIMEOUT_TU), + fst_session_timeout_handler, NULL, s); + s->stt_armed = TRUE; +} + + +static void fst_session_stt_disarm(struct fst_session *s) +{ + if (s->stt_armed) { + eloop_cancel_timeout(fst_session_timeout_handler, NULL, s); + s->stt_armed = FALSE; + } +} + + +static Boolean fst_session_is_in_transition(struct fst_session *s) +{ + /* See spec, 10.32.2.2 Transitioning between states */ + return s->stt_armed; +} + + +static int fst_session_is_in_progress(struct fst_session *s) +{ + return s->state != FST_SESSION_STATE_INITIAL; +} + + +static int fst_session_is_ready_pending(struct fst_session *s) +{ + return s->state == FST_SESSION_STATE_SETUP_COMPLETION && + fst_session_is_in_transition(s); +} + + +static int fst_session_is_ready(struct fst_session *s) +{ + return s->state == FST_SESSION_STATE_SETUP_COMPLETION && + !fst_session_is_in_transition(s); +} + + +static int fst_session_is_switch_requested(struct fst_session *s) +{ + return s->state == FST_SESSION_STATE_TRANSITION_DONE && + fst_session_is_in_transition(s); +} + + +static struct fst_session * +fst_find_session_in_progress(const u8 *peer_addr, struct fst_group *g) +{ + struct fst_session *s; + + foreach_fst_session(s) { + if (s->group == g && + (os_memcmp(s->data.old_peer_addr, peer_addr, + ETH_ALEN) == 0 || + os_memcmp(s->data.new_peer_addr, peer_addr, + ETH_ALEN) == 0) && + fst_session_is_in_progress(s)) + return s; + } + + return NULL; +} + + +static void fst_session_reset_ex(struct fst_session *s, enum fst_reason reason) +{ + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = reason, + }, + }; + + if (s->state == FST_SESSION_STATE_SETUP_COMPLETION || + s->state == FST_SESSION_STATE_TRANSITION_DONE) + fst_session_tear_down_setup(s); + fst_session_stt_disarm(s); + os_memset(&s->data, 0, sizeof(s->data)); + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); +} + + +static int fst_session_send_action(struct fst_session *s, Boolean old_iface, + const void *payload, size_t size, + const struct wpabuf *extra_buf) +{ + size_t len; + int res; + struct wpabuf *buf; + u8 action; + struct fst_iface *iface = + old_iface ? s->data.old_iface : s->data.new_iface; + + WPA_ASSERT(payload != NULL); + WPA_ASSERT(size != 0); + + action = *(const u8 *) payload; + + WPA_ASSERT(action <= FST_ACTION_MAX_SUPPORTED); + + if (!iface) { + fst_printf_session(s, MSG_ERROR, + "no %s interface for FST Action '%s' sending", + old_iface ? "old" : "new", + fst_action_names[action]); + return -1; + } + + len = sizeof(u8) /* category */ + size; + if (extra_buf) + len += wpabuf_size(extra_buf); + + buf = wpabuf_alloc(len); + if (!buf) { + fst_printf_session(s, MSG_ERROR, + "cannot allocate buffer of %zu bytes for FST Action '%s' sending", + len, fst_action_names[action]); + return -1; + } + + wpabuf_put_u8(buf, WLAN_ACTION_FST); + wpabuf_put_data(buf, payload, size); + if (extra_buf) + wpabuf_put_buf(buf, extra_buf); + + res = fst_iface_send_action(iface, + old_iface ? s->data.old_peer_addr : + s->data.new_peer_addr, buf); + if (res < 0) + fst_printf_siface(s, iface, MSG_ERROR, + "failed to send FST Action '%s'", + fst_action_names[action]); + else + fst_printf_siface(s, iface, MSG_DEBUG, "FST Action '%s' sent", + fst_action_names[action]); + wpabuf_free(buf); + + return res; +} + + +static int fst_session_send_tear_down(struct fst_session *s) +{ + struct fst_tear_down td; + int res; + + if (!fst_session_is_in_progress(s)) { + fst_printf_session(s, MSG_ERROR, "No FST setup to tear down"); + return -1; + } + + WPA_ASSERT(s->data.old_iface != NULL); + WPA_ASSERT(s->data.new_iface != NULL); + + os_memset(&td, 0, sizeof(td)); + + td.action = FST_ACTION_TEAR_DOWN; + td.fsts_id = host_to_le32(s->data.fsts_id); + + res = fst_session_send_action(s, TRUE, &td, sizeof(td), NULL); + if (!res) + fst_printf_sframe(s, TRUE, MSG_INFO, "FST TearDown sent"); + else + fst_printf_sframe(s, TRUE, MSG_ERROR, + "failed to send FST TearDown"); + + return res; +} + + +static void fst_session_handle_setup_request(struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len) +{ + struct fst_session *s; + const struct fst_setup_req *req; + struct fst_iface *new_iface = NULL; + struct fst_group *g; + u8 new_iface_peer_addr[ETH_ALEN]; + const struct wpabuf *peer_mbies; + size_t plen; + + if (frame_len < IEEE80211_HDRLEN + 1 + sizeof(*req)) { + fst_printf_iface(iface, MSG_WARNING, + "FST Request dropped: too short (%zu < %zu)", + frame_len, + IEEE80211_HDRLEN + 1 + sizeof(*req)); + return; + } + plen = frame_len - IEEE80211_HDRLEN - 1; + req = (const struct fst_setup_req *) + (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); + if (req->stie.element_id != WLAN_EID_SESSION_TRANSITION || + req->stie.length < 11) { + fst_printf_iface(iface, MSG_WARNING, + "FST Request dropped: invalid STIE"); + return; + } + + if (req->stie.new_band_id == req->stie.old_band_id) { + fst_printf_iface(iface, MSG_WARNING, + "FST Request dropped: new and old band IDs are the same"); + return; + } + + g = fst_iface_get_group(iface); + + if (plen > sizeof(*req)) { + fst_iface_update_mb_ie(iface, mgmt->sa, (const u8 *) (req + 1), + plen - sizeof(*req)); + fst_printf_iface(iface, MSG_INFO, + "FST Request: MB IEs updated for " MACSTR, + MAC2STR(mgmt->sa)); + } + + peer_mbies = fst_iface_get_peer_mb_ie(iface, mgmt->sa); + if (peer_mbies) { + new_iface = fst_group_get_new_iface_by_stie_and_mbie( + g, wpabuf_head(peer_mbies), wpabuf_len(peer_mbies), + &req->stie, new_iface_peer_addr); + if (new_iface) + fst_printf_iface(iface, MSG_INFO, + "FST Request: new iface (%s:" MACSTR + ") found by MB IEs", + fst_iface_get_name(new_iface), + MAC2STR(new_iface_peer_addr)); + } + + if (!new_iface) { + new_iface = fst_group_find_new_iface_by_stie( + g, iface, mgmt->sa, &req->stie, + new_iface_peer_addr); + if (new_iface) + fst_printf_iface(iface, MSG_INFO, + "FST Request: new iface (%s:" MACSTR + ") found by others", + fst_iface_get_name(new_iface), + MAC2STR(new_iface_peer_addr)); + } + + if (!new_iface) { + fst_printf_iface(iface, MSG_WARNING, + "FST Request dropped: new iface not found"); + return; + } + + s = fst_find_session_in_progress(mgmt->sa, g); + if (s) { + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = REASON_SETUP, + }, + }; + + /* + * 10.32.2.2 Transitioning between states: + * Upon receipt of an FST Setup Request frame, the responder + * shall respond with an FST Setup Response frame unless it has + * a pending FST Setup Request frame addressed to the initiator + * and the responder has a numerically larger MAC address than + * the initiator’s MAC address, in which case, the responder + * shall delete the received FST Setup Request. + */ + if (os_memcmp(mgmt->da, mgmt->sa, ETH_ALEN) > 0) { + fst_printf_session(s, MSG_WARNING, + "FST Request dropped due to MAC comparison (our MAC is " + MACSTR ")", + MAC2STR(mgmt->da)); + return; + } + + if (!fst_session_is_ready_pending(s)) { + fst_printf_session(s, MSG_WARNING, + "FST Request from " MACSTR + " dropped due to inappropriate state %s", + MAC2STR(mgmt->da), + fst_session_state_name(s->state)); + return; + } + + + /* + * If FST Setup Request arrived with the same FSTS ID as one we + * initialized before, it means the other side either didn't + * receive our FST Request or skipped it for some reason (for + * example, due to numerical MAC comparison). + * + * In this case, there's no need to tear down the session. + * Moreover, as FSTS ID is the same, the other side will + * associate this tear down with the session it initiated that + * will break the sync. + */ + if (le_to_host32(req->stie.fsts_id) != s->data.fsts_id) + fst_session_send_tear_down(s); + else + fst_printf_session(s, MSG_WARNING, + "Skipping TearDown as the FST request has the same FSTS ID as initiated"); + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + fst_session_stt_disarm(s); + fst_printf_session(s, MSG_WARNING, "reset due to FST request"); + } + + s = fst_session_create(g); + if (!s) { + fst_printf(MSG_WARNING, + "FST Request dropped: cannot create session for %s and %s", + fst_iface_get_name(iface), + fst_iface_get_name(new_iface)); + return; + } + + fst_session_set_iface(s, iface, TRUE); + fst_session_set_peer_addr(s, mgmt->sa, TRUE); + fst_session_set_iface(s, new_iface, FALSE); + fst_session_set_peer_addr(s, new_iface_peer_addr, FALSE); + fst_session_set_llt(s, FST_LLT_VAL_TO_MS(le_to_host32(req->llt))); + s->data.pending_setup_req_dlgt = req->dialog_token; + s->data.fsts_id = le_to_host32(req->stie.fsts_id); + + fst_session_stt_arm(s); + + fst_session_notify_ctrl(s, EVENT_FST_SETUP, NULL); + + fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION, NULL); +} + + +static void fst_session_handle_setup_response(struct fst_session *s, + struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len) +{ + const struct fst_setup_res *res; + size_t plen = frame_len - IEEE80211_HDRLEN - 1; + enum hostapd_hw_mode hw_mode; + u8 channel; + union fst_session_state_switch_extra evext = { + .to_initial = {0}, + }; + + if (iface != s->data.old_iface) { + fst_printf_session(s, MSG_WARNING, + "FST Response dropped: %s is not the old iface", + fst_iface_get_name(iface)); + return; + } + + if (!fst_session_is_ready_pending(s)) { + fst_printf_session(s, MSG_WARNING, + "FST Response dropped due to wrong state: %s", + fst_session_state_name(s->state)); + return; + } + + if (plen < sizeof(*res)) { + fst_printf_session(s, MSG_WARNING, + "Too short FST Response dropped"); + return; + } + res = (const struct fst_setup_res *) + (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); + if (res->stie.element_id != WLAN_EID_SESSION_TRANSITION || + res->stie.length < 11) { + fst_printf_iface(iface, MSG_WARNING, + "FST Response dropped: invalid STIE"); + return; + } + + if (res->dialog_token != s->data.pending_setup_req_dlgt) { + fst_printf_session(s, MSG_WARNING, + "FST Response dropped due to wrong dialog token (%u != %u)", + s->data.pending_setup_req_dlgt, + res->dialog_token); + return; + } + + if (res->status_code == WLAN_STATUS_SUCCESS && + le_to_host32(res->stie.fsts_id) != s->data.fsts_id) { + fst_printf_session(s, MSG_WARNING, + "FST Response dropped due to wrong FST Session ID (%u)", + le_to_host32(res->stie.fsts_id)); + return; + } + + fst_session_stt_disarm(s); + + if (res->status_code != WLAN_STATUS_SUCCESS) { + /* + * 10.32.2.2 Transitioning between states + * The initiator shall set the STT to the value of the + * FSTSessionTimeOut field at ... and at each ACK frame sent in + * response to a received FST Setup Response with the Status + * Code field equal to PENDING_ADMITTING_FST_SESSION or + * PENDING_GAP_IN_BA_WINDOW. + */ + evext.to_initial.reason = REASON_REJECT; + evext.to_initial.reject_code = res->status_code; + evext.to_initial.initiator = FST_INITIATOR_REMOTE; + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + fst_printf_session(s, MSG_WARNING, + "FST Setup rejected by remote side with status %u", + res->status_code); + return; + } + + fst_iface_get_channel_info(s->data.new_iface, &hw_mode, &channel); + + if (fst_hw_mode_to_band(hw_mode) != res->stie.new_band_id) { + evext.to_initial.reason = REASON_ERROR_PARAMS; + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + fst_printf_session(s, MSG_WARNING, + "invalid FST Setup parameters"); + fst_session_tear_down_setup(s); + return; + } + + fst_printf_session(s, MSG_INFO, + "%s: FST Setup established for %s (llt=%u)", + fst_iface_get_name(s->data.old_iface), + fst_iface_get_name(s->data.new_iface), + s->data.llt_ms); + + fst_session_notify_ctrl(s, EVENT_FST_ESTABLISHED, NULL); + + if (s->data.llt_ms == FST_LLT_SWITCH_IMMEDIATELY) + fst_session_initiate_switch(s); +} + + +static void fst_session_handle_tear_down(struct fst_session *s, + struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len) +{ + const struct fst_tear_down *td; + size_t plen = frame_len - IEEE80211_HDRLEN - 1; + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = REASON_TEARDOWN, + .initiator = FST_INITIATOR_REMOTE, + }, + }; + + if (plen < sizeof(*td)) { + fst_printf_session(s, MSG_WARNING, + "Too short FST Tear Down dropped"); + return; + } + td = (const struct fst_tear_down *) + (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); + + if (le_to_host32(td->fsts_id) != s->data.fsts_id) { + fst_printf_siface(s, iface, MSG_WARNING, + "tear down for wrong FST Setup ID (%u)", + le_to_host32(td->fsts_id)); + return; + } + + fst_session_stt_disarm(s); + + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); +} + + +static void fst_session_handle_ack_request(struct fst_session *s, + struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len) +{ + const struct fst_ack_req *req; + size_t plen = frame_len - IEEE80211_HDRLEN - 1; + struct fst_ack_res res; + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = REASON_SWITCH, + .initiator = FST_INITIATOR_REMOTE, + }, + }; + + if (!fst_session_is_ready(s) && !fst_session_is_switch_requested(s)) { + fst_printf_siface(s, iface, MSG_ERROR, + "cannot initiate switch due to wrong session state (%s)", + fst_session_state_name(s->state)); + return; + } + + WPA_ASSERT(s->data.new_iface != NULL); + + if (iface != s->data.new_iface) { + fst_printf_siface(s, iface, MSG_ERROR, + "Ack received on wrong interface"); + return; + } + + if (plen < sizeof(*req)) { + fst_printf_session(s, MSG_WARNING, + "Too short FST Ack Request dropped"); + return; + } + req = (const struct fst_ack_req *) + (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); + + if (le_to_host32(req->fsts_id) != s->data.fsts_id) { + fst_printf_siface(s, iface, MSG_WARNING, + "Ack for wrong FST Setup ID (%u)", + le_to_host32(req->fsts_id)); + return; + } + + os_memset(&res, 0, sizeof(res)); + + res.action = FST_ACTION_ACK_RESPONSE; + res.dialog_token = req->dialog_token; + res.fsts_id = req->fsts_id; + + if (!fst_session_send_action(s, FALSE, &res, sizeof(res), NULL)) { + fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Response sent"); + fst_session_stt_disarm(s); + fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE, + NULL); + fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED, + NULL); + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + } +} + + +static void +fst_session_handle_ack_response(struct fst_session *s, + struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len) +{ + const struct fst_ack_res *res; + size_t plen = frame_len - IEEE80211_HDRLEN - 1; + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = REASON_SWITCH, + .initiator = FST_INITIATOR_LOCAL, + }, + }; + + if (!fst_session_is_switch_requested(s)) { + fst_printf_siface(s, iface, MSG_ERROR, + "Ack Response in inappropriate session state (%s)", + fst_session_state_name(s->state)); + return; + } + + WPA_ASSERT(s->data.new_iface != NULL); + + if (iface != s->data.new_iface) { + fst_printf_siface(s, iface, MSG_ERROR, + "Ack response received on wrong interface"); + return; + } + + if (plen < sizeof(*res)) { + fst_printf_session(s, MSG_WARNING, + "Too short FST Ack Response dropped"); + return; + } + res = (const struct fst_ack_res *) + (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); + + if (le_to_host32(res->fsts_id) != s->data.fsts_id) { + fst_printf_siface(s, iface, MSG_ERROR, + "Ack response for wrong FST Setup ID (%u)", + le_to_host32(res->fsts_id)); + return; + } + + fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED, NULL); + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + + fst_session_stt_disarm(s); +} + + +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"); + return NULL; + } + + s = os_zalloc(sizeof(*s)); + if (!s) { + fst_printf(MSG_ERROR, "Cannot allocate new session object"); + return NULL; + } + + s->id = id; + s->group = g; + s->state = FST_SESSION_STATE_INITIAL; + + s->data.llt_ms = FST_LLT_MS_DEFAULT; + + fst_printf(MSG_INFO, "Session %u created", s->id); + + dl_list_add_tail(&global_sessions_list, &s->global_sessions_lentry); + + foreach_fst_ctrl_call(on_session_added, s); + + return s; +} + + +void fst_session_set_iface(struct fst_session *s, struct fst_iface *iface, + Boolean is_old) +{ + if (is_old) + s->data.old_iface = iface; + else + s->data.new_iface = iface; + +} + + +void fst_session_set_llt(struct fst_session *s, u32 llt) +{ + s->data.llt_ms = llt; +} + + +void fst_session_set_peer_addr(struct fst_session *s, const u8 *addr, + Boolean is_old) +{ + u8 *a = is_old ? s->data.old_peer_addr : s->data.new_peer_addr; + + os_memcpy(a, addr, ETH_ALEN); +} + + +int fst_session_initiate_setup(struct fst_session *s) +{ + struct fst_setup_req req; + int res; + u32 fsts_id; + u8 dialog_token; + struct fst_session *_s; + + if (fst_session_is_in_progress(s)) { + fst_printf_session(s, MSG_ERROR, "Session in progress"); + return -EINVAL; + } + + if (is_zero_ether_addr(s->data.old_peer_addr)) { + fst_printf_session(s, MSG_ERROR, "No old peer MAC address"); + return -EINVAL; + } + + if (is_zero_ether_addr(s->data.new_peer_addr)) { + fst_printf_session(s, MSG_ERROR, "No new peer MAC address"); + return -EINVAL; + } + + if (!s->data.old_iface) { + fst_printf_session(s, MSG_ERROR, "No old interface defined"); + return -EINVAL; + } + + if (!s->data.new_iface) { + fst_printf_session(s, MSG_ERROR, "No new interface defined"); + return -EINVAL; + } + + if (s->data.new_iface == s->data.old_iface) { + fst_printf_session(s, MSG_ERROR, + "Same interface set as old and new"); + return -EINVAL; + } + + if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr)) { + fst_printf_session(s, MSG_ERROR, + "The preset old peer address is not connected"); + return -EINVAL; + } + + if (!fst_iface_is_connected(s->data.new_iface, s->data.new_peer_addr)) { + fst_printf_session(s, MSG_ERROR, + "The preset new peer address is not connected"); + return -EINVAL; + } + + _s = fst_find_session_in_progress(s->data.old_peer_addr, s->group); + if (_s) { + fst_printf_session(s, MSG_ERROR, + "There is another session in progress (old): %u", + _s->id); + return -EINVAL; + } + + _s = fst_find_session_in_progress(s->data.new_peer_addr, s->group); + if (_s) { + fst_printf_session(s, MSG_ERROR, + "There is another session in progress (new): %u", + _s->id); + return -EINVAL; + } + + dialog_token = fst_group_assign_dialog_token(s->group); + fsts_id = fst_group_assign_fsts_id(s->group); + + os_memset(&req, 0, sizeof(req)); + + fst_printf_siface(s, s->data.old_iface, MSG_INFO, + "initiating FST setup for %s (llt=%u ms)", + fst_iface_get_name(s->data.new_iface), s->data.llt_ms); + + req.action = FST_ACTION_SETUP_REQUEST; + req.dialog_token = dialog_token; + req.llt = host_to_le32(FST_LLT_MS_TO_VAL(s->data.llt_ms)); + /* 8.4.2.147 Session Transition element */ + req.stie.element_id = WLAN_EID_SESSION_TRANSITION; + req.stie.length = sizeof(req.stie) - 2; + req.stie.fsts_id = host_to_le32(fsts_id); + req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); + + req.stie.new_band_id = fst_iface_get_band_id(s->data.new_iface); + req.stie.new_band_op = 1; + req.stie.new_band_setup = 0; + + req.stie.old_band_id = fst_iface_get_band_id(s->data.old_iface); + req.stie.old_band_op = 1; + req.stie.old_band_setup = 0; + + res = fst_session_send_action(s, TRUE, &req, sizeof(req), + fst_iface_get_mbie(s->data.old_iface)); + if (!res) { + s->data.fsts_id = fsts_id; + s->data.pending_setup_req_dlgt = dialog_token; + fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Request sent"); + fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION, + NULL); + + fst_session_stt_arm(s); + } + + return res; +} + + +int fst_session_respond(struct fst_session *s, u8 status_code) +{ + struct fst_setup_res res; + enum hostapd_hw_mode hw_mode; + u8 channel; + + if (!fst_session_is_ready_pending(s)) { + fst_printf_session(s, MSG_ERROR, "incorrect state: %s", + fst_session_state_name(s->state)); + return -EINVAL; + } + + if (is_zero_ether_addr(s->data.old_peer_addr)) { + fst_printf_session(s, MSG_ERROR, "No peer MAC address"); + return -EINVAL; + } + + if (!s->data.old_iface) { + fst_printf_session(s, MSG_ERROR, "No old interface defined"); + return -EINVAL; + } + + if (!s->data.new_iface) { + fst_printf_session(s, MSG_ERROR, "No new interface defined"); + return -EINVAL; + } + + if (s->data.new_iface == s->data.old_iface) { + fst_printf_session(s, MSG_ERROR, + "Same interface set as old and new"); + return -EINVAL; + } + + if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr)) { + fst_printf_session(s, MSG_ERROR, + "The preset peer address is not in the peer list"); + return -EINVAL; + } + + fst_session_stt_disarm(s); + + os_memset(&res, 0, sizeof(res)); + + res.action = FST_ACTION_SETUP_RESPONSE; + res.dialog_token = s->data.pending_setup_req_dlgt; + res.status_code = status_code; + + res.stie.element_id = WLAN_EID_SESSION_TRANSITION; + res.stie.length = sizeof(res.stie) - 2; + + if (status_code == WLAN_STATUS_SUCCESS) { + res.stie.fsts_id = s->data.fsts_id; + res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); + + fst_iface_get_channel_info(s->data.new_iface, &hw_mode, + &channel); + res.stie.new_band_id = fst_hw_mode_to_band(hw_mode); + res.stie.new_band_op = 1; + res.stie.new_band_setup = 0; + + fst_iface_get_channel_info(s->data.old_iface, &hw_mode, + &channel); + res.stie.old_band_id = fst_hw_mode_to_band(hw_mode); + res.stie.old_band_op = 1; + res.stie.old_band_setup = 0; + + fst_printf_session(s, MSG_INFO, + "%s: FST Setup Request accepted for %s (llt=%u)", + fst_iface_get_name(s->data.old_iface), + fst_iface_get_name(s->data.new_iface), + s->data.llt_ms); + } else { + fst_printf_session(s, MSG_WARNING, + "%s: FST Setup Request rejected with code %d", + fst_iface_get_name(s->data.old_iface), + status_code); + } + + if (fst_session_send_action(s, TRUE, &res, sizeof(res), + fst_iface_get_mbie(s->data.old_iface))) { + fst_printf_sframe(s, TRUE, MSG_ERROR, + "cannot send FST Setup Response with code %d", + status_code); + return -EINVAL; + } + + fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Response sent"); + + if (status_code != WLAN_STATUS_SUCCESS) { + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = REASON_REJECT, + .reject_code = status_code, + .initiator = FST_INITIATOR_LOCAL, + }, + }; + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + } + + return 0; +} + + +int fst_session_initiate_switch(struct fst_session *s) +{ + struct fst_ack_req req; + int res; + u8 dialog_token; + + if (!fst_session_is_ready(s)) { + fst_printf_session(s, MSG_ERROR, + "cannot initiate switch due to wrong setup state (%d)", + s->state); + return -1; + } + + dialog_token = fst_group_assign_dialog_token(s->group); + + WPA_ASSERT(s->data.new_iface != NULL); + WPA_ASSERT(s->data.old_iface != NULL); + + fst_printf_session(s, MSG_INFO, "initiating FST switch: %s => %s", + fst_iface_get_name(s->data.old_iface), + fst_iface_get_name(s->data.new_iface)); + + os_memset(&req, 0, sizeof(req)); + + req.action = FST_ACTION_ACK_REQUEST; + req.dialog_token = dialog_token; + req.fsts_id = host_to_le32(s->data.fsts_id); + + res = fst_session_send_action(s, FALSE, &req, sizeof(req), NULL); + if (!res) { + fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Request sent"); + fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE, + NULL); + fst_session_stt_arm(s); + } else { + fst_printf_sframe(s, FALSE, MSG_ERROR, + "Cannot send FST Ack Request"); + } + + return res; +} + + +void fst_session_handle_action(struct fst_session *s, + struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len) +{ + switch (mgmt->u.action.u.fst_action.action) { + case FST_ACTION_SETUP_REQUEST: + WPA_ASSERT(0); + break; + case FST_ACTION_SETUP_RESPONSE: + fst_session_handle_setup_response(s, iface, mgmt, frame_len); + break; + case FST_ACTION_TEAR_DOWN: + fst_session_handle_tear_down(s, iface, mgmt, frame_len); + break; + case FST_ACTION_ACK_REQUEST: + fst_session_handle_ack_request(s, iface, mgmt, frame_len); + break; + case FST_ACTION_ACK_RESPONSE: + fst_session_handle_ack_response(s, iface, mgmt, frame_len); + break; + case FST_ACTION_ON_CHANNEL_TUNNEL: + default: + fst_printf_sframe(s, FALSE, MSG_ERROR, + "Unsupported FST Action frame"); + break; + } +} + + +int fst_session_tear_down_setup(struct fst_session *s) +{ + int res; + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = REASON_TEARDOWN, + .initiator = FST_INITIATOR_LOCAL, + }, + }; + + res = fst_session_send_tear_down(s); + + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + + return res; +} + + +void fst_session_reset(struct fst_session *s) +{ + fst_session_reset_ex(s, REASON_RESET); +} + + +void fst_session_delete(struct fst_session *s) +{ + fst_printf(MSG_INFO, "Session %u deleted", s->id); + dl_list_del(&s->global_sessions_lentry); + foreach_fst_ctrl_call(on_session_removed, s); + os_free(s); +} + + +struct fst_group * fst_session_get_group(struct fst_session *s) +{ + return s->group; +} + + +struct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old) +{ + return is_old ? s->data.old_iface : s->data.new_iface; +} + + +u32 fst_session_get_id(struct fst_session *s) +{ + return s->id; +} + + +const u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old) +{ + return is_old ? s->data.old_peer_addr : s->data.new_peer_addr; +} + + +u32 fst_session_get_llt(struct fst_session *s) +{ + return s->data.llt_ms; +} + + +enum fst_session_state fst_session_get_state(struct fst_session *s) +{ + return s->state; +} + + +struct fst_session * fst_session_get_by_id(u32 id) +{ + struct fst_session *s; + + foreach_fst_session(s) { + if (id == s->id) + return s; + } + + return NULL; +} + + +void fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx) +{ + struct fst_session *s; + + foreach_fst_session(s) { + if (!g || s->group == g) + clb(s->group, s, ctx); + } +} + + +void fst_session_on_action_rx(struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct fst_session *s; + + if (len < IEEE80211_HDRLEN + 2 || + mgmt->u.action.category != WLAN_ACTION_FST) { + fst_printf_iface(iface, MSG_ERROR, + "invalid Action frame received"); + return; + } + + if (mgmt->u.action.u.fst_action.action <= FST_ACTION_MAX_SUPPORTED) { + fst_printf_iface(iface, MSG_DEBUG, + "FST Action '%s' received!", + fst_action_names[mgmt->u.action.u.fst_action.action]); + } else { + fst_printf_iface(iface, MSG_WARNING, + "unknown FST Action (%u) received!", + mgmt->u.action.u.fst_action.action); + return; + } + + if (mgmt->u.action.u.fst_action.action == FST_ACTION_SETUP_REQUEST) { + fst_session_handle_setup_request(iface, mgmt, len); + return; + } + + s = fst_find_session_in_progress(mgmt->sa, fst_iface_get_group(iface)); + if (s) { + fst_session_handle_action(s, iface, mgmt, len); + } else { + fst_printf_iface(iface, MSG_WARNING, + "FST Action '%s' dropped: no session in progress found", + fst_action_names[mgmt->u.action.u.fst_action.action]); + } +} + + +int fst_session_set_str_ifname(struct fst_session *s, const char *ifname, + Boolean is_old) +{ + struct fst_group *g = fst_session_get_group(s); + struct fst_iface *i; + + i = fst_group_get_iface_by_name(g, ifname); + if (!i) { + fst_printf_session(s, MSG_WARNING, + "Cannot set iface %s: no such iface within group '%s'", + ifname, fst_group_get_id(g)); + return -1; + } + + fst_session_set_iface(s, i, is_old); + + return 0; +} + + +int fst_session_set_str_peer_addr(struct fst_session *s, const char *mac, + Boolean is_old) +{ + u8 peer_addr[ETH_ALEN]; + int res = fst_read_peer_addr(mac, peer_addr); + + if (res) + return res; + + fst_session_set_peer_addr(s, peer_addr, is_old); + + return 0; +} + + +int fst_session_set_str_llt(struct fst_session *s, const char *llt_str) +{ + char *endp; + long int llt = strtol(llt_str, &endp, 0); + + if (*endp || llt < 0 || (unsigned long int) llt > FST_MAX_LLT_MS) { + fst_printf_session(s, MSG_WARNING, + "Cannot set llt %s: Invalid llt value (1..%u expected)", + llt_str, FST_MAX_LLT_MS); + return -1; + } + fst_session_set_llt(s, (u32) llt); + + return 0; +} + + +void fst_session_global_on_iface_detached(struct fst_iface *iface) +{ + struct fst_session *s; + + foreach_fst_session(s) { + if (fst_session_is_in_progress(s) && + (s->data.new_iface == iface || + s->data.old_iface == iface)) + fst_session_reset_ex(s, REASON_DETACH_IFACE); + } +} + + +struct fst_session * fst_session_global_get_first_by_group(struct fst_group *g) +{ + struct fst_session *s; + + foreach_fst_session(s) { + if (s->group == g) + return s; + } + + return NULL; +} + + +#ifdef CONFIG_FST_TEST + +static int get_group_fill_session(struct fst_group **g, struct fst_session *s) +{ + const u8 *old_addr, *new_addr; + struct fst_get_peer_ctx *ctx; + + os_memset(s, 0, sizeof(*s)); + foreach_fst_group(*g) { + s->data.new_iface = fst_group_first_iface(*g); + if (s->data.new_iface) + break; + } + if (!s->data.new_iface) + return -EINVAL; + + s->data.old_iface = dl_list_entry(s->data.new_iface->group_lentry.next, + struct fst_iface, group_lentry); + if (!s->data.old_iface) + return -EINVAL; + + old_addr = fst_iface_get_peer_first(s->data.old_iface, &ctx, TRUE); + if (!old_addr) + return -EINVAL; + + new_addr = fst_iface_get_peer_first(s->data.new_iface, &ctx, TRUE); + if (!new_addr) + return -EINVAL; + + os_memcpy(s->data.old_peer_addr, old_addr, ETH_ALEN); + os_memcpy(s->data.new_peer_addr, new_addr, ETH_ALEN); + + return 0; +} + + +#define FST_MAX_COMMAND_WORD_NAME_LENGTH 16 + +int fst_test_req_send_fst_request(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_setup_req req; + struct fst_session s; + struct fst_group *g; + enum hostapd_hw_mode hw_mode; + u8 channel; + char additional_param[FST_MAX_COMMAND_WORD_NAME_LENGTH]; + + if (params[0] != ' ') + return -EINVAL; + params++; + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return -EINVAL; + + if (get_group_fill_session(&g, &s)) + return -EINVAL; + + req.action = FST_ACTION_SETUP_REQUEST; + req.dialog_token = g->dialog_token; + req.llt = host_to_le32(FST_LLT_MS_DEFAULT); + /* 8.4.2.147 Session Transition element */ + req.stie.element_id = WLAN_EID_SESSION_TRANSITION; + req.stie.length = sizeof(req.stie) - 2; + req.stie.fsts_id = host_to_le32(fsts_id); + req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); + + fst_iface_get_channel_info(s.data.new_iface, &hw_mode, &channel); + req.stie.new_band_id = fst_hw_mode_to_band(hw_mode); + req.stie.new_band_op = 1; + req.stie.new_band_setup = 0; + + fst_iface_get_channel_info(s.data.old_iface, &hw_mode, &channel); + req.stie.old_band_id = fst_hw_mode_to_band(hw_mode); + req.stie.old_band_op = 1; + req.stie.old_band_setup = 0; + + if (!fst_read_next_text_param(endp, additional_param, + sizeof(additional_param), &endp)) { + if (!os_strcasecmp(additional_param, FST_CTR_PVAL_BAD_NEW_BAND)) + req.stie.new_band_id = req.stie.old_band_id; + } + + return fst_session_send_action(&s, TRUE, &req, sizeof(req), + s.data.old_iface->mb_ie); +} + + +int fst_test_req_send_fst_response(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_setup_res res; + struct fst_session s; + struct fst_group *g; + enum hostapd_hw_mode hw_mode; + u8 status_code; + u8 channel; + char response[FST_MAX_COMMAND_WORD_NAME_LENGTH]; + struct fst_session *_s; + + if (params[0] != ' ') + return -EINVAL; + params++; + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return -EINVAL; + + if (get_group_fill_session(&g, &s)) + return -EINVAL; + + status_code = WLAN_STATUS_SUCCESS; + if (!fst_read_next_text_param(endp, response, sizeof(response), + &endp)) { + if (!os_strcasecmp(response, FST_CS_PVAL_RESPONSE_REJECT)) + status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION; + } + + os_memset(&res, 0, sizeof(res)); + + res.action = FST_ACTION_SETUP_RESPONSE; + /* + * If some session has just received an FST Setup Request, then + * use the correct dialog token copied from this request. + */ + _s = fst_find_session_in_progress(fst_session_get_peer_addr(&s, TRUE), + g); + res.dialog_token = (_s && fst_session_is_ready_pending(_s)) ? + _s->data.pending_setup_req_dlgt : g->dialog_token; + res.status_code = status_code; + + res.stie.element_id = WLAN_EID_SESSION_TRANSITION; + res.stie.length = sizeof(res.stie) - 2; + + if (res.status_code == WLAN_STATUS_SUCCESS) { + res.stie.fsts_id = fsts_id; + res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); + + fst_iface_get_channel_info(s.data.new_iface, &hw_mode, + &channel); + res.stie.new_band_id = fst_hw_mode_to_band(hw_mode); + res.stie.new_band_op = 1; + res.stie.new_band_setup = 0; + + fst_iface_get_channel_info(s.data.old_iface, &hw_mode, + &channel); + res.stie.old_band_id = fst_hw_mode_to_band(hw_mode); + res.stie.old_band_op = 1; + res.stie.old_band_setup = 0; + } + + if (!fst_read_next_text_param(endp, response, sizeof(response), + &endp)) { + if (!os_strcasecmp(response, FST_CTR_PVAL_BAD_NEW_BAND)) + res.stie.new_band_id = res.stie.old_band_id; + } + + return fst_session_send_action(&s, TRUE, &res, sizeof(res), + s.data.old_iface->mb_ie); +} + + +int fst_test_req_send_ack_request(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_ack_req req; + struct fst_session s; + struct fst_group *g; + + if (params[0] != ' ') + return -EINVAL; + params++; + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return -EINVAL; + + if (get_group_fill_session(&g, &s)) + return -EINVAL; + + os_memset(&req, 0, sizeof(req)); + req.action = FST_ACTION_ACK_REQUEST; + req.dialog_token = g->dialog_token; + req.fsts_id = fsts_id; + + return fst_session_send_action(&s, FALSE, &req, sizeof(req), NULL); +} + + +int fst_test_req_send_ack_response(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_ack_res res; + struct fst_session s; + struct fst_group *g; + + if (params[0] != ' ') + return -EINVAL; + params++; + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return -EINVAL; + + if (get_group_fill_session(&g, &s)) + return -EINVAL; + + os_memset(&res, 0, sizeof(res)); + res.action = FST_ACTION_ACK_RESPONSE; + res.dialog_token = g->dialog_token; + res.fsts_id = fsts_id; + + return fst_session_send_action(&s, FALSE, &res, sizeof(res), NULL); +} + + +int fst_test_req_send_tear_down(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_tear_down td; + struct fst_session s; + struct fst_group *g; + + if (params[0] != ' ') + return -EINVAL; + params++; + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return -EINVAL; + + if (get_group_fill_session(&g, &s)) + return -EINVAL; + + os_memset(&td, 0, sizeof(td)); + td.action = FST_ACTION_TEAR_DOWN; + td.fsts_id = fsts_id; + + return fst_session_send_action(&s, TRUE, &td, sizeof(td), NULL); +} + + +u32 fst_test_req_get_fsts_id(const char *params) +{ + int sid; + Boolean is_valid; + char *endp; + struct fst_session *s; + + if (params[0] != ' ') + return FST_FSTS_ID_NOT_FOUND; + params++; + sid = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return FST_FSTS_ID_NOT_FOUND; + + s = fst_session_get_by_id(sid); + if (!s) + return FST_FSTS_ID_NOT_FOUND; + + return s->data.fsts_id; +} + + +int fst_test_req_get_local_mbies(const char *request, char *buf, size_t buflen) +{ + char *endp; + char ifname[FST_MAX_COMMAND_WORD_NAME_LENGTH]; + struct fst_group *g; + struct fst_iface *iface; + + if (request[0] != ' ') + return -EINVAL; + request++; + if (fst_read_next_text_param(request, ifname, sizeof(ifname), &endp) || + !*ifname) + goto problem; + g = dl_list_first(&fst_global_groups_list, struct fst_group, + global_groups_lentry); + if (!g) + goto problem; + iface = fst_group_get_iface_by_name(g, ifname); + if (!iface || !iface->mb_ie) + goto problem; + return wpa_snprintf_hex(buf, buflen, wpabuf_head(iface->mb_ie), + wpabuf_len(iface->mb_ie)); + +problem: + return os_snprintf(buf, buflen, "FAIL\n"); +} + +#endif /* CONFIG_FST_TEST */ diff --git a/src/fst/fst_session.h b/src/fst/fst_session.h new file mode 100644 index 0000000000000..1162de4b367a1 --- /dev/null +++ b/src/fst/fst_session.h @@ -0,0 +1,80 @@ +/* + * FST module - FST Session related definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_SESSION_H +#define FST_SESSION_H + +#define FST_DEFAULT_SESSION_TIMEOUT_TU 255 /* u8 */ + +struct fst_iface; +struct fst_group; +struct fst_session; +enum fst_session_state; + +int fst_session_global_init(void); +void fst_session_global_deinit(void); +void fst_session_global_on_iface_detached(struct fst_iface *iface); +struct fst_session * +fst_session_global_get_first_by_group(struct fst_group *g); + +struct fst_session * fst_session_create(struct fst_group *g); +void fst_session_set_iface(struct fst_session *s, struct fst_iface *iface, + Boolean is_old); +void fst_session_set_llt(struct fst_session *s, u32 llt); +void fst_session_set_peer_addr(struct fst_session *s, const u8 *addr, + Boolean is_old); +int fst_session_initiate_setup(struct fst_session *s); +int fst_session_respond(struct fst_session *s, u8 status_code); +int fst_session_initiate_switch(struct fst_session *s); +void fst_session_handle_action(struct fst_session *s, struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len); +int fst_session_tear_down_setup(struct fst_session *s); +void fst_session_reset(struct fst_session *s); +void fst_session_delete(struct fst_session *s); + +struct fst_group * fst_session_get_group(struct fst_session *s); +struct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old); +const u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old); +u32 fst_session_get_id(struct fst_session *s); +u32 fst_session_get_llt(struct fst_session *s); +enum fst_session_state fst_session_get_state(struct fst_session *s); + +struct fst_session *fst_session_get_by_id(u32 id); + +typedef void (*fst_session_enum_clb)(struct fst_group *g, struct fst_session *s, + void *ctx); + +void fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx); + +void fst_session_on_action_rx(struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, size_t len); + + +int fst_session_set_str_ifname(struct fst_session *s, const char *ifname, + Boolean is_old); +int fst_session_set_str_peer_addr(struct fst_session *s, const char *mac, + Boolean is_old); +int fst_session_set_str_llt(struct fst_session *s, const char *llt_str); + +#ifdef CONFIG_FST_TEST + +#define FST_FSTS_ID_NOT_FOUND ((u32) -1) + +int fst_test_req_send_fst_request(const char *params); +int fst_test_req_send_fst_response(const char *params); +int fst_test_req_send_ack_request(const char *params); +int fst_test_req_send_ack_response(const char *params); +int fst_test_req_send_tear_down(const char *params); +u32 fst_test_req_get_fsts_id(const char *params); +int fst_test_req_get_local_mbies(const char *request, char *buf, + size_t buflen); + +#endif /* CONFIG_FST_TEST */ + +#endif /* FST_SESSION_H */ diff --git a/src/l2_packet/Makefile b/src/l2_packet/Makefile index adfd3dfd5b9be..47925b790c74f 100644 --- a/src/l2_packet/Makefile +++ b/src/l2_packet/Makefile @@ -1,8 +1,16 @@ -all: - @echo Nothing to be made. +all: libl2_packet.a clean: - rm -f *~ *.o *.d *.gcno *.gcda *.gcov + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libl2_packet.a install: @echo Nothing to be made. + +include ../lib.rules + +LIB_OBJS = l2_packet_linux.o + +libl2_packet.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/src/p2p/Makefile b/src/p2p/Makefile index adfd3dfd5b9be..5587fcf281d3a 100644 --- a/src/p2p/Makefile +++ b/src/p2p/Makefile @@ -1,8 +1,29 @@ -all: - @echo Nothing to be made. +all: libp2p.a clean: - rm -f *~ *.o *.d *.gcno *.gcda *.gcov + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libp2p.a install: @echo Nothing to be made. + +include ../lib.rules + +CFLAGS += -DCONFIG_WIFI_DISPLAY +CFLAGS += -DCONFIG_WPS_NFC + +LIB_OBJS= \ + p2p_build.o \ + p2p.o \ + p2p_dev_disc.o \ + p2p_go_neg.o \ + p2p_group.o \ + p2p_invitation.o \ + p2p_parse.o \ + p2p_pd.o \ + p2p_sd.o \ + p2p_utils.o + +libp2p.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 6adb3dc2049fb..767706c01d6b3 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -48,9 +48,8 @@ static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx); #define P2P_PEER_EXPIRATION_AGE 60 #endif /* P2P_PEER_EXPIRATION_AGE */ -#define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2) -static void p2p_expire_peers(struct p2p_data *p2p) +void p2p_expire_peers(struct p2p_data *p2p) { struct p2p_device *dev, *n; struct os_reltime now; @@ -103,15 +102,6 @@ static void p2p_expire_peers(struct p2p_data *p2p) } -static void p2p_expiration_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct p2p_data *p2p = eloop_ctx; - p2p_expire_peers(p2p); - eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, - p2p_expiration_timeout, p2p, NULL); -} - - static const char * p2p_state_txt(int state) { switch (state) { @@ -297,7 +287,7 @@ static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc) return; } - ies = p2p_build_probe_resp_ies(p2p); + ies = p2p_build_probe_resp_ies(p2p, NULL, 0); if (ies == NULL) return; @@ -346,7 +336,7 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout) return 0; } - ies = p2p_build_probe_resp_ies(p2p); + ies = p2p_build_probe_resp_ies(p2p, NULL, 0); if (ies == NULL) return -1; @@ -468,7 +458,8 @@ static void p2p_copy_client_info(struct p2p_device *dev, static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr, const u8 *go_interface_addr, int freq, - const u8 *gi, size_t gi_len) + const u8 *gi, size_t gi_len, + struct os_reltime *rx_time) { struct p2p_group_info info; size_t c; @@ -536,10 +527,11 @@ static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr, os_memcpy(dev->interface_addr, cli->p2p_interface_addr, ETH_ALEN); - os_get_reltime(&dev->last_seen); + os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime)); os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN); os_memcpy(dev->member_in_go_iface, go_interface_addr, ETH_ALEN); + dev->flags |= P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT; } return 0; @@ -758,26 +750,35 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, /* * Update the device entry only if the new peer - * entry is newer than the one previously stored. + * entry is newer than the one previously stored, or if + * the device was previously seen as a P2P Client in a group + * and the new entry isn't older than a threshold. */ if (dev->last_seen.sec > 0 && - os_reltime_before(rx_time, &dev->last_seen)) { - p2p_dbg(p2p, "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u)", + os_reltime_before(rx_time, &dev->last_seen) && + (!(dev->flags & P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT) || + os_reltime_expired(&dev->last_seen, rx_time, + P2P_DEV_GROUP_CLIENT_RESP_THRESHOLD))) { + p2p_dbg(p2p, + "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u flags=0x%x)", (unsigned int) rx_time->sec, (unsigned int) rx_time->usec, (unsigned int) dev->last_seen.sec, - (unsigned int) dev->last_seen.usec); + (unsigned int) dev->last_seen.usec, + dev->flags); p2p_parse_free(&msg); return -1; } os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime)); - dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY); + dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY | + P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT); if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0) os_memcpy(dev->interface_addr, addr, ETH_ALEN); if (msg.ssid && + msg.ssid[1] <= sizeof(dev->oper_ssid) && (msg.ssid[1] != P2P_WILDCARD_SSID_LEN || os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) != 0)) { @@ -843,7 +844,8 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, if (scan_res) { p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, - msg.group_info, msg.group_info_len); + msg.group_info, msg.group_info_len, + rx_time); } p2p_parse_free(&msg); @@ -1127,8 +1129,10 @@ static int p2ps_gen_hash(struct p2p_data *p2p, const char *str, u8 *hash) adv_array = (u8 *) str_buf; adv_len = os_strlen(str); + if (adv_len >= sizeof(str_buf)) + return 0; - for (i = 0; str[i] && i < adv_len; i++) { + for (i = 0; i < adv_len; i++) { if (str[i] >= 'A' && str[i] <= 'Z') str_buf[i] = str[i] - 'A' + 'a'; else @@ -1182,27 +1186,25 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, * An empty seek string means no hash values, but still an ASP * search. */ + p2p_dbg(p2p, "ASP search"); p2p->p2ps_seek_count = 0; p2p->p2ps_seek = 1; } else if (seek && seek_count <= P2P_MAX_QUERY_HASH) { u8 buf[P2PS_HASH_LEN]; - int i; + int i, count = 0; - p2p->p2ps_seek_count = seek_count; for (i = 0; i < seek_count; i++) { if (!p2ps_gen_hash(p2p, seek[i], buf)) continue; - /* If asking for wildcard, don't do others */ - if (os_memcmp(buf, p2p->wild_card_hash, - P2PS_HASH_LEN) == 0) { - p2p->p2ps_seek_count = 0; - break; - } - - os_memcpy(&p2p->query_hash[i * P2PS_HASH_LEN], buf, - P2PS_HASH_LEN); + p2p_dbg(p2p, "Seek service %s hash " MACSTR, + seek[i], MAC2STR(buf)); + os_memcpy(&p2p->p2ps_seek_hash[count * P2PS_HASH_LEN], + buf, P2PS_HASH_LEN); + count++; } + + p2p->p2ps_seek_count = count; p2p->p2ps_seek = 1; } else { p2p->p2ps_seek_count = 0; @@ -1212,7 +1214,8 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, /* Special case to perform wildcard search */ if (p2p->p2ps_seek_count == 0 && p2p->p2ps_seek) { p2p->p2ps_seek_count = 1; - os_memcpy(&p2p->query_hash, p2p->wild_card_hash, P2PS_HASH_LEN); + os_memcpy(&p2p->p2ps_seek_hash, p2p->wild_card_hash, + P2PS_HASH_LEN); } p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; @@ -1380,7 +1383,7 @@ static int p2p_prepare_channel_pref(struct p2p_data *p2p, static void p2p_prepare_channel_best(struct p2p_data *p2p) { u8 op_class, op_channel; - const int op_classes_5ghz[] = { 124, 115, 0 }; + const int op_classes_5ghz[] = { 124, 125, 115, 0 }; const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; const int op_classes_vht[] = { 128, 0 }; @@ -2147,7 +2150,9 @@ int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps) } -struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) +struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p, + const u8 *query_hash, + u8 query_count) { struct wpabuf *buf; u8 *len; @@ -2162,7 +2167,7 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]); - if (p2p->query_count) + if (query_count) extra += MAX_SVC_ADV_IE_LEN; buf = wpabuf_alloc(1000 + extra); @@ -2199,9 +2204,8 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) p2p_buf_add_device_info(buf, p2p, NULL); p2p_buf_update_ie_hdr(buf, len); - if (p2p->query_count) { - p2p_buf_add_service_instance(buf, p2p, p2p->query_count, - p2p->query_hash, + if (query_count) { + p2p_buf_add_service_instance(buf, p2p, query_count, query_hash, p2p->p2ps_adv_list); } @@ -2212,18 +2216,21 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash) { struct p2ps_advertisement *adv_data; + int any_wfa; p2p_dbg(p2p, "ASP find - ASP list: %p", p2p->p2ps_adv_list); - /* Wildcard always matches if we have actual services */ - if (os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0) - return p2p->p2ps_adv_list != NULL; + /* Wildcard org.wi-fi.wfds matches any WFA spec defined service */ + any_wfa = os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0; adv_data = p2p->p2ps_adv_list; while (adv_data) { - p2p_dbg(p2p, "ASP hash: %x =? %x", hash[0], adv_data->hash[0]); if (os_memcmp(hash, adv_data->hash, P2PS_HASH_LEN) == 0) - return 1; + return 1; /* exact hash match */ + if (any_wfa && + os_strncmp(adv_data->svc_name, P2PS_WILD_HASH_STR, + os_strlen(P2PS_WILD_HASH_STR)) == 0) + return 1; /* WFA service match */ adv_data = adv_data->next; } @@ -2233,13 +2240,15 @@ static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash) static enum p2p_probe_req_status p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, - const u8 *bssid, const u8 *ie, size_t ie_len) + const u8 *bssid, const u8 *ie, size_t ie_len, + unsigned int rx_freq) { struct ieee802_11_elems elems; struct wpabuf *buf; struct ieee80211_mgmt *resp; struct p2p_message msg; struct wpabuf *ies; + u8 channel, op_class; if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) == ParseFailed) { @@ -2291,53 +2300,42 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, return P2P_PREQ_NOT_P2P; } - p2p->p2ps_svc_found = 0; - if (msg.service_hash && msg.service_hash_count) { const u8 *hash = msg.service_hash; - u8 *dest = p2p->query_hash; u8 i; + int p2ps_svc_found = 0; + + p2p_dbg(p2p, "in_listen=%d drv_in_listen=%d when received P2PS Probe Request at %u MHz; own Listen channel %u, pending listen freq %u MHz", + p2p->in_listen, p2p->drv_in_listen, rx_freq, + p2p->cfg->channel, p2p->pending_listen_freq); + + if (!p2p->in_listen && !p2p->drv_in_listen && + p2p->pending_listen_freq && rx_freq && + rx_freq != p2p->pending_listen_freq) { + p2p_dbg(p2p, "Do not reply to Probe Request frame that was received on %u MHz while waiting to start Listen state on %u MHz", + rx_freq, p2p->pending_listen_freq); + p2p_parse_free(&msg); + return P2P_PREQ_NOT_LISTEN; + } - p2p->query_count = 0; for (i = 0; i < msg.service_hash_count; i++) { if (p2p_service_find_asp(p2p, hash)) { - p2p->p2ps_svc_found = 1; - - if (!os_memcmp(hash, p2p->wild_card_hash, - P2PS_HASH_LEN)) { - /* We found match(es) but wildcard - * will return all */ - p2p->query_count = 1; - os_memcpy(p2p->query_hash, hash, - P2PS_HASH_LEN); - break; - } - - /* Save each matching hash */ - if (p2p->query_count < P2P_MAX_QUERY_HASH) { - os_memcpy(dest, hash, P2PS_HASH_LEN); - dest += P2PS_HASH_LEN; - p2p->query_count++; - } else { - /* We found match(es) but too many to - * return all */ - p2p->query_count = 0; - break; - } + p2p_dbg(p2p, "Service Hash match found: " + MACSTR, MAC2STR(hash)); + p2ps_svc_found = 1; + break; } hash += P2PS_HASH_LEN; } - p2p_dbg(p2p, "ASP adv found: %d", p2p->p2ps_svc_found); - /* Probed hash unknown */ - if (!p2p->p2ps_svc_found) { + if (!p2ps_svc_found) { + p2p_dbg(p2p, "No Service Hash match found"); p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } } else { /* This is not a P2PS Probe Request */ - p2p->query_count = 0; p2p_dbg(p2p, "No P2PS Hash in Probe Request"); if (!p2p->in_listen || !p2p->drv_in_listen) { @@ -2366,11 +2364,11 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } - p2p_parse_free(&msg); if (!p2p->cfg->send_probe_resp) { /* Response generated elsewhere */ p2p_dbg(p2p, "Probe Resp generated elsewhere - do not generate additional response"); + p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } @@ -2382,7 +2380,9 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, * really only used for discovery purposes, not to learn exact BSS * parameters. */ - ies = p2p_build_probe_resp_ies(p2p); + ies = p2p_build_probe_resp_ies(p2p, msg.service_hash, + msg.service_hash_count); + p2p_parse_free(&msg); if (ies == NULL) return P2P_PREQ_NOT_PROCESSED; @@ -2392,8 +2392,8 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, return P2P_PREQ_NOT_PROCESSED; } - resp = NULL; - resp = wpabuf_put(buf, resp->u.probe_resp.variable - (u8 *) resp); + resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt, + u.probe_resp.variable)); resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_PROBE_RESP << 4)); @@ -2422,32 +2422,50 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, wpabuf_put_u8(buf, 480 / 5); wpabuf_put_u8(buf, 540 / 5); + if (!rx_freq) { + channel = p2p->cfg->channel; + } else if (p2p_freq_to_channel(rx_freq, &op_class, &channel)) { + wpabuf_free(ies); + wpabuf_free(buf); + return P2P_PREQ_NOT_PROCESSED; + } + wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS); wpabuf_put_u8(buf, 1); - wpabuf_put_u8(buf, p2p->cfg->channel); + wpabuf_put_u8(buf, channel); wpabuf_put_buf(buf, ies); wpabuf_free(ies); - p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf); + p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf, rx_freq); wpabuf_free(buf); - return P2P_PREQ_NOT_PROCESSED; + return P2P_PREQ_PROCESSED; } enum p2p_probe_req_status p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, - const u8 *bssid, const u8 *ie, size_t ie_len) + const u8 *bssid, const u8 *ie, size_t ie_len, + unsigned int rx_freq) { enum p2p_probe_req_status res; p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len); - res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len); - p2p->query_count = 0; + res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len, rx_freq); + if (res != P2P_PREQ_PROCESSED && res != P2P_PREQ_NOT_PROCESSED) + return res; + /* + * Activate a pending GO Negotiation/Invite flow if a received Probe + * Request frame is from an expected peer. Some devices may share the + * same address for P2P and non-P2P STA running simultaneously. The + * P2P_PREQ_PROCESSED and P2P_PREQ_NOT_PROCESSED p2p_reply_probe() + * return values verified above ensure we are handling a Probe Request + * frame from a P2P peer. + */ if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) && p2p->go_neg_peer && os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) @@ -2457,7 +2475,7 @@ p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, p2p_dbg(p2p, "Found GO Negotiation peer - try to start GO negotiation from timeout"); eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL); - return P2P_PREQ_PROCESSED; + return res; } if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) && @@ -2469,7 +2487,7 @@ p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, p2p_dbg(p2p, "Found Invite peer - try to start Invite from timeout"); eloop_cancel_timeout(p2p_invite_start, p2p, NULL); eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL); - return P2P_PREQ_PROCESSED; + return res; } return res; @@ -2484,10 +2502,21 @@ static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid, size_t tmplen; int res; u8 group_capab; + struct p2p_message msg; if (p2p_ie == NULL) return 0; /* WLAN AP is not a P2P manager */ + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg) < 0) + return 0; + + p2p_dbg(p2p, "BSS P2P manageability %s", + msg.manageability ? "enabled" : "disabled"); + + if (!msg.manageability) + return 0; + /* * (Re)Association Request - P2P IE * P2P Capability attribute (shall be present) @@ -2650,13 +2679,14 @@ int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id) int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, const char *adv_str, u8 svc_state, u16 config_methods, - const char *svc_info) + const char *svc_info, const u8 *cpt_priority) { struct p2ps_advertisement *adv_data, *tmp, **prev; u8 buf[P2PS_HASH_LEN]; size_t adv_data_len, adv_len, info_len = 0; + int i; - if (!p2p || !adv_str || !adv_str[0]) + if (!p2p || !adv_str || !adv_str[0] || !cpt_priority) return -1; if (!(config_methods & p2p->cfg->config_methods)) { @@ -2685,6 +2715,11 @@ int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, adv_data->auto_accept = (u8) auto_accept; os_memcpy(adv_data->svc_name, adv_str, adv_len); + for (i = 0; cpt_priority[i] && i < P2PS_FEATURE_CAPAB_CPT_MAX; i++) { + adv_data->cpt_priority[i] = cpt_priority[i]; + adv_data->cpt_mask |= cpt_priority[i]; + } + if (svc_info && info_len) { adv_data->svc_info = &adv_data->svc_name[adv_len + 1]; os_memcpy(adv_data->svc_info, svc_info, info_len); @@ -2723,13 +2758,33 @@ int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, inserted: p2p_dbg(p2p, - "Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s'", - adv_id, adv_data->config_methods, svc_state, adv_str); + "Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s' cpt_mask=0x%x", + adv_id, adv_data->config_methods, svc_state, adv_str, + adv_data->cpt_mask); return 0; } +void p2p_service_flush_asp(struct p2p_data *p2p) +{ + struct p2ps_advertisement *adv, *prev; + + if (!p2p) + return; + + adv = p2p->p2ps_adv_list; + while (adv) { + prev = adv; + adv = adv->next; + os_free(prev); + } + + p2p->p2ps_adv_list = NULL; + p2p_dbg(p2p, "All ASP advertisements flushed"); +} + + int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr) { struct p2p_message msg; @@ -2861,9 +2916,6 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) dl_list_init(&p2p->devices); - eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, - p2p_expiration_timeout, p2p, NULL); - p2p->go_timeout = 100; p2p->client_timeout = 20; p2p->num_p2p_sd_queries = 0; @@ -2878,8 +2930,6 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) void p2p_deinit(struct p2p_data *p2p) { - struct p2ps_advertisement *adv, *prev; - #ifdef CONFIG_WIFI_DISPLAY wpabuf_free(p2p->wfd_ie_beacon); wpabuf_free(p2p->wfd_ie_probe_req); @@ -2894,7 +2944,6 @@ void p2p_deinit(struct p2p_data *p2p) wpabuf_free(p2p->wfd_coupled_sink_info); #endif /* CONFIG_WIFI_DISPLAY */ - eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL); eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); @@ -2908,18 +2957,12 @@ void p2p_deinit(struct p2p_data *p2p) os_free(p2p->cfg->serial_number); os_free(p2p->cfg->pref_chan); os_free(p2p->groups); - os_free(p2p->p2ps_prov); + p2ps_prov_free(p2p); wpabuf_free(p2p->sd_resp); os_free(p2p->after_scan_tx); p2p_remove_wps_vendor_extensions(p2p); os_free(p2p->no_go_freq.range); - - adv = p2p->p2ps_adv_list; - while (adv) { - prev = adv; - adv = adv->next; - os_free(prev); - } + p2p_service_flush_asp(p2p); os_free(p2p); } @@ -2937,6 +2980,7 @@ void p2p_flush(struct p2p_data *p2p) p2p_free_sd_queries(p2p); os_free(p2p->after_scan_tx); p2p->after_scan_tx = NULL; + p2p->ssid_set = 0; } @@ -4120,7 +4164,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, "country=%c%c\n" "oper_freq=%d\n" "req_config_methods=0x%x\n" - "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" + "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" "status=%d\n" "invitation_reqs=%u\n", (int) (now.sec - dev->last_seen.sec), @@ -4164,6 +4208,8 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, "[FORCE_FREQ]" : "", dev->flags & P2P_DEV_PD_FOR_JOIN ? "[PD_FOR_JOIN]" : "", + dev->flags & P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT ? + "[LAST_SEEN_AS_GROUP_CLIENT]" : "", dev->status, dev->invitation_reqs); if (os_snprintf_error(end - pos, res)) @@ -5215,6 +5261,7 @@ int p2p_process_nfc_connection_handover(struct p2p_data *p2p, if (!msg.oob_go_neg_channel) { p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included"); + p2p_parse_free(&msg); return -1; } @@ -5226,6 +5273,7 @@ int p2p_process_nfc_connection_handover(struct p2p_data *p2p, msg.oob_go_neg_channel[4]); if (freq < 0) { p2p_dbg(p2p, "Unknown peer OOB GO Neg channel"); + p2p_parse_free(&msg); return -1; } role = msg.oob_go_neg_channel[5]; @@ -5246,6 +5294,7 @@ int p2p_process_nfc_connection_handover(struct p2p_data *p2p, p2p->cfg->channel); if (freq < 0) { p2p_dbg(p2p, "Own listen channel not known"); + p2p_parse_free(&msg); return -1; } p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq); @@ -5334,3 +5383,20 @@ void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx) "Timeout on waiting peer to become ready for GO Negotiation"); p2p_go_neg_failed(p2p, -1); } + + +void p2p_set_own_pref_freq_list(struct p2p_data *p2p, + const unsigned int *pref_freq_list, + unsigned int size) +{ + unsigned int i; + + if (size > P2P_MAX_PREF_CHANNELS) + size = P2P_MAX_PREF_CHANNELS; + p2p->num_pref_freq = size; + for (i = 0; i < size; i++) { + p2p->pref_freq_list[i] = pref_freq_list[i]; + p2p_dbg(p2p, "Own preferred frequency list[%u]=%u MHz", + i, p2p->pref_freq_list[i]); + } +} diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index 2402db6a7eb94..b4060be477b6b 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -9,7 +9,8 @@ #ifndef P2P_H #define P2P_H -#include "wps/wps_defs.h" +#include "common/ieee802_11_defs.h" +#include "wps/wps.h" /* P2P ASP Setup Capability */ #define P2PS_SETUP_NONE 0 @@ -20,6 +21,12 @@ #define P2PS_WILD_HASH_STR "org.wi-fi.wfds" #define P2PS_HASH_LEN 6 #define P2P_MAX_QUERY_HASH 6 +#define P2PS_FEATURE_CAPAB_CPT_MAX 2 + +/** + * P2P_MAX_PREF_CHANNELS - Maximum number of preferred channels + */ +#define P2P_MAX_PREF_CHANNELS 100 /** * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes @@ -95,7 +102,7 @@ struct p2p_go_neg_results { /** * ssid - SSID of the group */ - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; /** * ssid_len - Length of SSID in octets @@ -155,6 +162,11 @@ struct p2p_go_neg_results { struct p2ps_provision { /** + * pd_seeker - P2PS provision discovery seeker role + */ + unsigned int pd_seeker:1; + + /** * status - Remote returned provisioning status code */ int status; @@ -195,6 +207,23 @@ struct p2ps_provision { u8 adv_mac[ETH_ALEN]; /** + * cpt_mask - Supported Coordination Protocol Transport mask + * + * A bitwise mask of supported ASP Coordination Protocol Transports. + * This property is set together and corresponds with cpt_priority. + */ + u8 cpt_mask; + + /** + * cpt_priority - Coordination Protocol Transport priority list + * + * Priorities of supported ASP Coordination Protocol Transports. + * This property is set together and corresponds with cpt_mask. + * The CPT priority list is 0 terminated. + */ + u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; + + /** * info - Vendor defined extra Provisioning information */ char info[0]; @@ -234,6 +263,23 @@ struct p2ps_advertisement { u8 hash[P2PS_HASH_LEN]; /** + * cpt_mask - supported Coordination Protocol Transport mask + * + * A bitwise mask of supported ASP Coordination Protocol Transports. + * This property is set together and corresponds with cpt_priority. + */ + u8 cpt_mask; + + /** + * cpt_priority - Coordination Protocol Transport priority list + * + * Priorities of supported ASP Coordinatin Protocol Transports. + * This property is set together and corresponds with cpt_mask. + * The CPT priority list is 0 terminated. + */ + u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; + + /** * svc_name - NULL Terminated UTF-8 Service Name, and svc_info storage */ char svc_name[0]; @@ -268,27 +314,27 @@ struct p2p_peer_info { /** * device_name - Device Name (0..32 octets encoded in UTF-8) */ - char device_name[33]; + char device_name[WPS_DEV_NAME_MAX_LEN + 1]; /** * manufacturer - Manufacturer (0..64 octets encoded in UTF-8) */ - char manufacturer[65]; + char manufacturer[WPS_MANUFACTURER_MAX_LEN + 1]; /** * model_name - Model Name (0..32 octets encoded in UTF-8) */ - char model_name[33]; + char model_name[WPS_MODEL_NAME_MAX_LEN + 1]; /** * model_number - Model Number (0..32 octets encoded in UTF-8) */ - char model_number[33]; + char model_number[WPS_MODEL_NUMBER_MAX_LEN + 1]; /** * serial_number - Serial Number (0..32 octets encoded in UTF-8) */ - char serial_number[33]; + char serial_number[WPS_SERIAL_NUMBER_MAX_LEN + 1]; /** * level - Signal level @@ -316,7 +362,7 @@ struct p2p_peer_info { * This list includes from 0 to 16 Secondary Device Types as indicated * by wps_sec_dev_type_list_len (8 * number of types). */ - u8 wps_sec_dev_type_list[128]; + u8 wps_sec_dev_type_list[WPS_SEC_DEV_TYPE_MAX_LEN]; /** * wps_sec_dev_type_list_len - Length of secondary device type list @@ -495,7 +541,7 @@ struct p2p_config { * This data will be added to the end of the SSID after the * DIRECT-<random two octets> prefix. */ - u8 ssid_postfix[32 - 9]; + u8 ssid_postfix[SSID_MAX_LEN - 9]; /** * ssid_postfix_len - Length of the ssid_postfix data @@ -569,12 +615,14 @@ struct p2p_config { * send_probe_resp - Transmit a Probe Response frame * @ctx: Callback context from cb_ctx * @buf: Probe Response frame (including the header and body) + * @freq: Forced frequency (in MHz) to use or 0. * Returns: 0 on success, -1 on failure * * This function is used to reply to Probe Request frames that were * indicated with a call to p2p_probe_req_rx(). The response is to be - * sent on the same channel or to be dropped if the driver is not - * anymore listening to Probe Request frames. + * sent on the same channel, unless otherwise specified, or to be + * dropped if the driver is not listening to Probe Request frames + * anymore. * * Alternatively, the responsibility for building the Probe Response * frames in Listen state may be in another system component in which @@ -585,7 +633,8 @@ struct p2p_config { * Request frames must be indicated by calling p2p_probe_req_rx() even * if this send_probe_resp() is not used. */ - int (*send_probe_resp)(void *ctx, const struct wpabuf *buf); + int (*send_probe_resp)(void *ctx, const struct wpabuf *buf, + unsigned int freq); /** * send_action - Transmit an Action frame @@ -704,6 +753,7 @@ struct p2p_config { * @ctx: Callback context from cb_ctx * @src: Source address of the message triggering this notification * @dev_passwd_id: WPS Device Password ID + * @go_intent: Peer's GO Intent * * This callback is used to notify that a P2P Device is requesting * group owner negotiation with us, but we do not have all the @@ -712,7 +762,8 @@ struct p2p_config { * PIN or PBC button press. This information can be provided with a * call to p2p_connect(). */ - void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id); + void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id, + u8 go_intent); /** * go_neg_completed - Notification of GO Negotiation results @@ -949,18 +1000,21 @@ struct p2p_config { /** * Determine if we have a persistent group we share with remote peer + * and allocate interface for this group if needed * @ctx: Callback context from cb_ctx * @addr: Peer device address to search for * @ssid: Persistent group SSID or %NULL if any * @ssid_len: Length of @ssid - * @go_dev_addr: Buffer for returning intended GO P2P Device Address + * @go_dev_addr: Buffer for returning GO P2P Device Address * @ret_ssid: Buffer for returning group SSID * @ret_ssid_len: Buffer for returning length of @ssid + * @intended_iface_addr: Buffer for returning intended iface address * Returns: 1 if a matching persistent group was found, 0 otherwise */ int (*get_persistent_group)(void *ctx, const u8 *addr, const u8 *ssid, size_t ssid_len, u8 *go_dev_addr, - u8 *ret_ssid, size_t *ret_ssid_len); + u8 *ret_ssid, size_t *ret_ssid_len, + u8 *intended_iface_addr); /** * Get information about a possible local GO role @@ -1001,7 +1055,8 @@ struct p2p_config { u8 conncap, int passwd_id, const u8 *persist_ssid, size_t persist_ssid_size, int response_done, - int prov_start, const char *session_info); + int prov_start, const char *session_info, + const u8 *feat_cap, size_t feat_cap_len); /** * prov_disc_resp_cb - Callback for indicating completion of PD Response @@ -1023,6 +1078,20 @@ struct p2p_config { * P2PS_SETUP_* bitmap is used as the parameters and return value. */ u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role); + + /** + * get_pref_freq_list - Get preferred frequency list for an interface + * @ctx: Callback context from cb_ctx + * @go: Whether the use if for GO role + * @len: Length of freq_list in entries (both IN and OUT) + * @freq_list: Buffer for returning the preferred frequencies (MHz) + * Returns: 0 on success, -1 on failure + * + * This function can be used to query the preferred frequency list from + * the driver specific to a particular interface type. + */ + int (*get_pref_freq_list)(void *ctx, int go, + unsigned int *len, unsigned int *freq_list); }; @@ -1460,11 +1529,13 @@ enum p2p_probe_req_status { * @bssid: BSSID if available or %NULL * @ie: Information elements from the Probe Request frame body * @ie_len: Length of ie buffer in octets + * @rx_freq: Probe Request frame RX frequency * Returns: value indicating the type and status of the probe request */ enum p2p_probe_req_status p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, - const u8 *bssid, const u8 *ie, size_t ie_len); + const u8 *bssid, const u8 *ie, size_t ie_len, + unsigned int rx_freq); /** * p2p_rx_action - Report received Action frame @@ -1607,7 +1678,7 @@ struct p2p_group_config { /** * ssid - Group SSID */ - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; /** * ssid_len - Length of SSID @@ -2214,7 +2285,7 @@ struct p2p_nfc_params { size_t oob_dev_pw_len; int go_freq; u8 go_dev_addr[ETH_ALEN]; - u8 go_ssid[32]; + u8 go_ssid[SSID_MAX_LEN]; size_t go_ssid_len; }; @@ -2240,8 +2311,33 @@ struct p2ps_advertisement * p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id); int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, const char *adv_str, u8 svc_state, - u16 config_methods, const char *svc_info); + u16 config_methods, const char *svc_info, + const u8 *cpt_priority); int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id); +void p2p_service_flush_asp(struct p2p_data *p2p); struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p); +/** + * p2p_expire_peers - Periodic cleanup function to expire peers + * @p2p: P2P module context from p2p_init() + * + * This is a cleanup function that the entity calling p2p_init() is + * expected to call periodically to clean up expired peer entries. + */ +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); + +/** + * p2p_group_get_common_freqs - Get the group common frequencies + * @group: P2P group context from p2p_group_init() + * @common_freqs: On return will hold the group common frequencies + * @num: On return will hold the number of group common frequencies + * Returns: 0 on success, -1 otherwise + */ +int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs, + unsigned int *num); + #endif /* P2P_H */ diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c index 92c920662edb3..793d28ba7bdd2 100644 --- a/src/p2p/p2p_build.c +++ b/src/p2p/p2p_build.c @@ -10,6 +10,7 @@ #include "common.h" #include "common/ieee802_11_defs.h" +#include "common/qca-vendor.h" #include "wps/wps_i.h" #include "p2p_i.h" @@ -109,6 +110,44 @@ void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country, } +void p2p_buf_add_pref_channel_list(struct wpabuf *buf, + const u32 *preferred_freq_list, + unsigned int size) +{ + unsigned int i, count = 0; + u8 op_class, op_channel; + + if (!size) + return; + + /* + * First, determine the number of P2P supported channels in the + * pref_freq_list returned from driver. This is needed for calculations + * of the vendor IE size. + */ + for (i = 0; i < size; i++) { + if (p2p_freq_to_channel(preferred_freq_list[i], &op_class, + &op_channel) == 0) + count++; + } + + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 4 + count * sizeof(u16)); + wpabuf_put_be24(buf, OUI_QCA); + wpabuf_put_u8(buf, QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST); + for (i = 0; i < size; i++) { + if (p2p_freq_to_channel(preferred_freq_list[i], &op_class, + &op_channel) < 0) { + wpa_printf(MSG_DEBUG, "Unsupported frequency %u MHz", + preferred_freq_list[i]); + continue; + } + wpabuf_put_u8(buf, op_class); + wpabuf_put_u8(buf, op_channel); + } +} + + void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country, struct p2p_channels *chan) { @@ -353,10 +392,10 @@ void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p) /* Service Hash */ wpabuf_put_u8(buf, P2P_ATTR_SERVICE_HASH); wpabuf_put_le16(buf, p2p->p2ps_seek_count * P2PS_HASH_LEN); - wpabuf_put_data(buf, p2p->query_hash, + wpabuf_put_data(buf, p2p->p2ps_seek_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN); wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash", - p2p->query_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN); + p2p->p2ps_seek_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN); } @@ -404,152 +443,221 @@ void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac) } -void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p, - u8 hash_count, const u8 *hash, - struct p2ps_advertisement *adv_list) +static int p2ps_wildcard_hash(struct p2p_data *p2p, + const u8 *hash, u8 hash_count) +{ + u8 i; + const u8 *test = hash; + + for (i = 0; i < hash_count; i++) { + if (os_memcmp(test, p2p->wild_card_hash, P2PS_HASH_LEN) == 0) + return 1; + test += P2PS_HASH_LEN; + } + + return 0; +} + + +static int p2p_wfa_service_adv(struct p2p_data *p2p) { struct p2ps_advertisement *adv; - struct wpabuf *tmp_buf; - u8 *tag_len = NULL, *ie_len = NULL; - size_t svc_len = 0, remaining = 0, total_len = 0; - if (!adv_list || !hash) - return; + for (adv = p2p->p2ps_adv_list; adv; adv = adv->next) { + if (os_strncmp(adv->svc_name, P2PS_WILD_HASH_STR, + os_strlen(P2PS_WILD_HASH_STR)) == 0) + return 1; + } - /* Allocate temp buffer, allowing for overflow of 1 instance */ - tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN); - if (!tmp_buf) - return; + return 0; +} - for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN; - adv = adv->next) { - u8 count = hash_count; - const u8 *test = hash; - while (count--) { - /* Check for wildcard */ - if (os_memcmp(test, p2p->wild_card_hash, - P2PS_HASH_LEN) == 0) { - total_len = MAX_SVC_ADV_LEN + 1; - goto wild_hash; - } +static int p2p_buf_add_service_info(struct wpabuf *buf, struct p2p_data *p2p, + u32 adv_id, u16 config_methods, + const char *svc_name, u8 **ie_len, u8 **pos, + size_t *total_len, u8 *attr_len) +{ + size_t svc_len; + size_t remaining; + size_t info_len; - if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0) - goto hash_match; + p2p_dbg(p2p, "Add service info for %s (adv_id=%u)", svc_name, adv_id); + svc_len = os_strlen(svc_name); + info_len = sizeof(adv_id) + sizeof(config_methods) + sizeof(u8) + + svc_len; - test += P2PS_HASH_LEN; - } + if (info_len + *total_len > MAX_SVC_ADV_LEN) { + p2p_dbg(p2p, + "Unsufficient buffer, failed to add advertised service info"); + return -1; + } - /* No matches found - Skip this Adv Instance */ - continue; - -hash_match: - if (!tag_len) { - tag_len = p2p_buf_add_ie_hdr(tmp_buf); - remaining = 255 - 4; - if (!ie_len) { - wpabuf_put_u8(tmp_buf, - P2P_ATTR_ADVERTISED_SERVICE); - ie_len = wpabuf_put(tmp_buf, sizeof(u16)); - remaining -= (sizeof(u8) + sizeof(u16)); - } - } + if (svc_len > 255) { + p2p_dbg(p2p, + "Invalid service name length (%u bytes), failed to add advertised service info", + (unsigned int) svc_len); + return -1; + } - svc_len = os_strlen(adv->svc_name); + if (*ie_len) { + int ie_data_len = (*pos - *ie_len) - 1; - if (7 + svc_len + total_len > MAX_SVC_ADV_LEN) { - /* Can't fit... return wildcard */ - total_len = MAX_SVC_ADV_LEN + 1; - break; + if (ie_data_len < 0 || ie_data_len > 255) { + p2p_dbg(p2p, + "Invalid IE length, failed to add advertised service info"); + return -1; } + remaining = 255 - ie_data_len; + } else { + /* + * Adding new P2P IE header takes 6 extra bytes: + * - 2 byte IE header (1 byte IE id and 1 byte length) + * - 4 bytes of IE_VENDOR_TYPE are reduced from 255 below + */ + *ie_len = p2p_buf_add_ie_hdr(buf); + remaining = 255 - 4; + } - if (remaining <= (sizeof(adv->id) + - sizeof(adv->config_methods))) { - size_t front = remaining; - size_t back = (sizeof(adv->id) + - sizeof(adv->config_methods)) - front; - u8 holder[sizeof(adv->id) + - sizeof(adv->config_methods)]; - - /* This works even if front or back == 0 */ - WPA_PUT_LE32(holder, adv->id); - WPA_PUT_BE16(&holder[sizeof(adv->id)], - adv->config_methods); - wpabuf_put_data(tmp_buf, holder, front); - p2p_buf_update_ie_hdr(tmp_buf, tag_len); - tag_len = p2p_buf_add_ie_hdr(tmp_buf); - wpabuf_put_data(tmp_buf, &holder[front], back); - remaining = 255 - (sizeof(adv->id) + - sizeof(adv->config_methods)) - back; - } else { - wpabuf_put_le32(tmp_buf, adv->id); - wpabuf_put_be16(tmp_buf, adv->config_methods); - remaining -= (sizeof(adv->id) + - sizeof(adv->config_methods)); - } + if (remaining < sizeof(u32) + sizeof(u16) + sizeof(u8)) { + /* + * Split adv_id, config_methods, and svc_name_len between two + * IEs. + */ + size_t front = remaining; + size_t back = sizeof(u32) + sizeof(u16) + sizeof(u8) - front; + u8 holder[sizeof(u32) + sizeof(u16) + sizeof(u8)]; - /* We are guaranteed at least one byte for svc_len */ - wpabuf_put_u8(tmp_buf, svc_len); - remaining -= sizeof(u8); - - if (remaining < svc_len) { - size_t front = remaining; - size_t back = svc_len - front; - - wpabuf_put_data(tmp_buf, adv->svc_name, front); - p2p_buf_update_ie_hdr(tmp_buf, tag_len); - tag_len = p2p_buf_add_ie_hdr(tmp_buf); - - /* In rare cases, we must split across 3 attributes */ - if (back > 255 - 4) { - wpabuf_put_data(tmp_buf, - &adv->svc_name[front], 255 - 4); - back -= 255 - 4; - front += 255 - 4; - p2p_buf_update_ie_hdr(tmp_buf, tag_len); - tag_len = p2p_buf_add_ie_hdr(tmp_buf); - } - - wpabuf_put_data(tmp_buf, &adv->svc_name[front], back); - remaining = 255 - 4 - back; - } else { - wpabuf_put_data(tmp_buf, adv->svc_name, svc_len); - remaining -= svc_len; - } + WPA_PUT_LE32(holder, adv_id); + WPA_PUT_BE16(&holder[sizeof(u32)], config_methods); + holder[sizeof(u32) + sizeof(u16)] = svc_len; + + if (front) + wpabuf_put_data(buf, holder, front); + + p2p_buf_update_ie_hdr(buf, *ie_len); + *ie_len = p2p_buf_add_ie_hdr(buf); - /* adv_id config_methods svc_string */ - total_len += sizeof(u32) + sizeof(u16) + sizeof(u8) + svc_len; + wpabuf_put_data(buf, &holder[front], back); + remaining = 255 - 4 - (sizeof(u32) + sizeof(u16) + sizeof(u8)) - + back; + } else { + wpabuf_put_le32(buf, adv_id); + wpabuf_put_be16(buf, config_methods); + wpabuf_put_u8(buf, svc_len); + remaining -= sizeof(adv_id) + sizeof(config_methods) + + sizeof(u8); } - if (tag_len) - p2p_buf_update_ie_hdr(tmp_buf, tag_len); + if (remaining < svc_len) { + /* split svc_name between two or three IEs */ + size_t front = remaining; + size_t back = svc_len - front; - if (ie_len) - WPA_PUT_LE16(ie_len, (u16) total_len); + if (front) + wpabuf_put_data(buf, svc_name, front); -wild_hash: - /* If all fit, return matching instances, otherwise the wildcard */ - if (total_len <= MAX_SVC_ADV_LEN) { - wpabuf_put_buf(buf, tmp_buf); + p2p_buf_update_ie_hdr(buf, *ie_len); + *ie_len = p2p_buf_add_ie_hdr(buf); + + /* In rare cases, we must split across 3 attributes */ + if (back > 255 - 4) { + wpabuf_put_data(buf, &svc_name[front], 255 - 4); + back -= 255 - 4; + front += 255 - 4; + p2p_buf_update_ie_hdr(buf, *ie_len); + *ie_len = p2p_buf_add_ie_hdr(buf); + } + + wpabuf_put_data(buf, &svc_name[front], back); + remaining = 255 - 4 - back; } else { - char *wild_card = P2PS_WILD_HASH_STR; - u8 wild_len; - - /* Insert wildcard instance */ - tag_len = p2p_buf_add_ie_hdr(buf); - wpabuf_put_u8(buf, P2P_ATTR_ADVERTISED_SERVICE); - ie_len = wpabuf_put(buf, sizeof(u16)); - - wild_len = (u8) os_strlen(wild_card); - wpabuf_put_le32(buf, 0); - wpabuf_put_be16(buf, 0); - wpabuf_put_u8(buf, wild_len); - wpabuf_put_data(buf, wild_card, wild_len); - - WPA_PUT_LE16(ie_len, 4 + 2 + 1 + wild_len); - p2p_buf_update_ie_hdr(buf, tag_len); + wpabuf_put_data(buf, svc_name, svc_len); + remaining -= svc_len; } + p2p_buf_update_ie_hdr(buf, *ie_len); + + /* set *ie_len to NULL if a new IE has to be added on the next call */ + if (!remaining) + *ie_len = NULL; + + /* set *pos to point to the next byte to update */ + *pos = wpabuf_put(buf, 0); + + *total_len += info_len; + WPA_PUT_LE16(attr_len, (u16) *total_len); + return 0; +} + + +void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p, + u8 hash_count, const u8 *hash, + struct p2ps_advertisement *adv_list) +{ + struct p2ps_advertisement *adv; + int p2ps_wildcard; + size_t total_len; + struct wpabuf *tmp_buf = NULL; + u8 *pos, *attr_len, *ie_len = NULL; + + if (!adv_list || !hash || !hash_count) + return; + + wpa_hexdump(MSG_DEBUG, "P2PS: Probe Request service hash values", + hash, hash_count * P2PS_HASH_LEN); + p2ps_wildcard = p2ps_wildcard_hash(p2p, hash, hash_count) && + p2p_wfa_service_adv(p2p); + + /* Allocate temp buffer, allowing for overflow of 1 instance */ + tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN); + if (!tmp_buf) + return; + + /* + * Attribute data can be split into a number of IEs. Start with the + * first IE and the attribute headers here. + */ + ie_len = p2p_buf_add_ie_hdr(tmp_buf); + + total_len = 0; + + wpabuf_put_u8(tmp_buf, P2P_ATTR_ADVERTISED_SERVICE); + attr_len = wpabuf_put(tmp_buf, sizeof(u16)); + WPA_PUT_LE16(attr_len, (u16) total_len); + p2p_buf_update_ie_hdr(tmp_buf, ie_len); + pos = wpabuf_put(tmp_buf, 0); + + if (p2ps_wildcard) { + /* org.wi-fi.wfds match found */ + p2p_buf_add_service_info(tmp_buf, p2p, 0, 0, P2PS_WILD_HASH_STR, + &ie_len, &pos, &total_len, attr_len); + } + + /* add advertised service info of matching services */ + for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN; + adv = adv->next) { + const u8 *test = hash; + u8 i; + + for (i = 0; i < hash_count; i++) { + /* exact name hash match */ + if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0 && + p2p_buf_add_service_info(tmp_buf, p2p, + adv->id, + adv->config_methods, + adv->svc_name, + &ie_len, &pos, + &total_len, + attr_len)) + break; + + test += P2PS_HASH_LEN; + } + } + + if (total_len) + wpabuf_put_buf(buf, tmp_buf); wpabuf_free(tmp_buf); } diff --git a/src/p2p/p2p_dev_disc.c b/src/p2p/p2p_dev_disc.c index 86bae1a2c0db8..98805fee2409b 100644 --- a/src/p2p/p2p_dev_disc.c +++ b/src/p2p/p2p_dev_disc.c @@ -314,7 +314,7 @@ void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, p2p_dbg(p2p, "Received GO Discoverability Request - remain awake for 100 TU"); - ies = p2p_build_probe_resp_ies(p2p); + ies = p2p_build_probe_resp_ies(p2p, NULL, 0); if (ies == NULL) return; diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index 98abf9d2293e9..83b43563d945f 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -185,6 +185,9 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, p2p->op_reg_class, p2p->op_channel); p2p_buf_update_ie_hdr(buf, len); + p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list, + p2p->num_pref_freq); + /* WPS IE with Device Password ID attribute */ pw_id = p2p_wps_method_pw_id(peer->wps_method); if (peer->oob_pw_id) @@ -312,7 +315,7 @@ 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) { + 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, @@ -379,7 +382,7 @@ void p2p_reselect_channel(struct p2p_data *p2p, int freq; u8 op_reg_class, op_channel; unsigned int i; - const int op_classes_5ghz[] = { 124, 115, 0 }; + const int op_classes_5ghz[] = { 124, 125, 115, 0 }; const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; const int op_classes_vht[] = { 128, 0 }; @@ -542,6 +545,195 @@ int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, } +static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go, + struct p2p_device *dev, + struct p2p_message *msg, + unsigned freq_list[], unsigned int size) +{ + u8 op_class, op_channel; + unsigned int oper_freq = 0, i, j; + int found = 0; + + p2p_dbg(p2p, + "Peer didn't provide a preferred frequency list, see if any of our preferred channels are supported by peer device"); + + /* + * Search for a common channel in our preferred frequency list which is + * 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. + */ + 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; + } + for (j = 0; j < msg->channel_list_len; j++) { + + if (op_channel != msg->channel_list[j]) + continue; + + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + found = 1; + break; + } + } + + if (found) { + p2p_dbg(p2p, + "Freq %d MHz is a preferred channel and is also supported by peer, use it as the operating channel", + oper_freq); + } else { + p2p_dbg(p2p, + "None of our preferred channels are supported by peer!. Use: %d MHz for oper_channel", + dev->oper_freq); + } +} + + +static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go, + struct p2p_device *dev, + struct p2p_message *msg, + unsigned freq_list[], unsigned int size) +{ + u8 op_class, op_channel; + unsigned int oper_freq = 0, i, j; + int found = 0; + + /* + * Peer device supports a Preferred Frequency List. + * Search for a common channel in the preferred frequency lists + * of both peer and local devices. + */ + for (i = 0; i < size && !found; i++) { + for (j = 2; j < (msg->pref_freq_list_len / 2); j++) { + oper_freq = p2p_channel_to_freq( + msg->pref_freq_list[2 * j], + 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; + } + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + found = 1; + break; + } + } + + if (found) { + p2p_dbg(p2p, + "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); + } +} + + +void p2p_check_pref_chan(struct p2p_data *p2p, int go, + struct p2p_device *dev, struct p2p_message *msg) +{ + unsigned int freq_list[P2P_MAX_PREF_CHANNELS], size; + unsigned int i; + u8 op_class, op_channel; + + /* + * Use the preferred channel list from the driver only if there is no + * forced_freq, e.g., P2P_CONNECT freq=..., and no preferred operating + * channel hardcoded in the configuration file. + */ + if (!p2p->cfg->get_pref_freq_list || p2p->cfg->num_pref_chan || + (dev->flags & P2P_DEV_FORCE_FREQ) || p2p->cfg->cfg_op_channel) + return; + + /* Obtain our preferred frequency list from driver based on P2P role. */ + size = P2P_MAX_PREF_CHANNELS; + if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, &size, + freq_list)) + return; + + /* + * Check if peer's preference of operating channel is in + * our preferred channel list. + */ + for (i = 0; i < size; i++) { + if (freq_list[i] == (unsigned int) dev->oper_freq) + break; + } + if (i != size) { + /* 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_dbg(p2p, + "Peer operating channel preference: %d MHz is not in our preferred channel list", + dev->oper_freq); + + /* + Check if peer's preferred channel list is + * _not_ included in the GO Negotiation Request or Invitation Request. + */ + if (msg->pref_freq_list_len == 0) + p2p_check_pref_chan_no_recv(p2p, go, dev, msg, freq_list, size); + else + p2p_check_pref_chan_recv(p2p, go, dev, msg, freq_list, size); +} + + void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { @@ -668,7 +860,9 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa, - msg.dev_password_id); + msg.dev_password_id, + msg.go_intent ? (*msg.go_intent >> 1) : + 0); } else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) { p2p_dbg(p2p, "Already in Group Formation with another peer"); status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; @@ -797,6 +991,12 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Peer operating channel preference: %d MHz", dev->oper_freq); + /* + * Use the driver preferred frequency list extension if + * supported. + */ + p2p_check_pref_chan(p2p, go, dev, &msg); + if (msg.config_timeout) { dev->go_timeout = msg.config_timeout[0]; dev->client_timeout = msg.config_timeout[1]; @@ -1148,6 +1348,13 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, if (go && p2p_go_select_channel(p2p, dev, &status) < 0) goto fail; + /* + * Use the driver preferred frequency list extension if local device is + * GO. + */ + if (go) + p2p_check_pref_chan(p2p, go, dev, &msg); + p2p_set_state(p2p, P2P_GO_NEG); p2p_clear_timeout(p2p); diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c index 41ca99faaf481..0d66993465686 100644 --- a/src/p2p/p2p_group.c +++ b/src/p2p/p2p_group.c @@ -1071,3 +1071,43 @@ void p2p_loop_on_all_groups(struct p2p_data *p2p, break; } } + + +int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs, + unsigned int *num) + +{ + struct p2p_channels intersect, res; + struct p2p_group_member *m; + + if (!group || !common_freqs || !num) + return -1; + + os_memset(&intersect, 0, sizeof(intersect)); + os_memset(&res, 0, sizeof(res)); + + p2p_channels_union(&intersect, &group->p2p->cfg->channels, + &intersect); + + p2p_channels_dump(group->p2p, + "Group common freqs before iterating members", + &intersect); + + for (m = group->members; m; m = m->next) { + struct p2p_device *dev; + + dev = p2p_get_device(group->p2p, m->dev_addr); + if (!dev) + continue; + + p2p_channels_intersect(&intersect, &dev->channels, &res); + intersect = res; + } + + p2p_channels_dump(group->p2p, "Group common channels", &intersect); + + os_memset(common_freqs, 0, *num * sizeof(int)); + *num = p2p_channels_to_freqs(&intersect, common_freqs, *num); + + return 0; +} diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 6af19ceda450e..0ce4058fe3e69 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -14,6 +14,12 @@ #define P2P_GO_NEG_CNF_MAX_RETRY_COUNT 1 +/* + * A threshold (in seconds) to prefer a direct Probe Response frame from a P2P + * Device over the P2P Client Info received from a GO. + */ +#define P2P_DEV_GROUP_CLIENT_RESP_THRESHOLD 1 + enum p2p_role_indication; /* @@ -47,6 +53,9 @@ struct p2p_device { * from Beacon/Probe Response), the interface address is stored here. * p2p_device_addr must still be set in such a case to the unique * identifier for the P2P Device. + * + * This field is also used during P2PS PD to store the intended GO + * address of the peer. */ u8 interface_addr[ETH_ALEN]; @@ -71,7 +80,7 @@ struct p2p_device { char country[3]; struct p2p_channels channels; int oper_freq; - u8 oper_ssid[32]; + u8 oper_ssid[SSID_MAX_LEN]; size_t oper_ssid_len; /** @@ -107,6 +116,8 @@ struct p2p_device { #define P2P_DEV_WAIT_INV_REQ_ACK BIT(19) #define P2P_DEV_P2PS_REPORTED BIT(20) #define P2P_DEV_PD_PEER_P2PS BIT(21) +#define P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT BIT(22) + unsigned int flags; int status; /* enum p2p_status_code */ @@ -322,7 +333,7 @@ struct p2p_data { /** * ssid - Selected SSID for GO Negotiation (if local end will be GO) */ - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; /** * ssid_len - ssid length in octets @@ -403,7 +414,7 @@ struct p2p_data { enum p2p_invite_role inv_role; u8 inv_bssid[ETH_ALEN]; int inv_bssid_set; - u8 inv_ssid[32]; + u8 inv_ssid[SSID_MAX_LEN]; size_t inv_ssid_len; u8 inv_sa[ETH_ALEN]; u8 inv_group_bssid[ETH_ALEN]; @@ -506,11 +517,9 @@ struct p2p_data { struct p2ps_advertisement *p2ps_adv_list; struct p2ps_provision *p2ps_prov; u8 wild_card_hash[P2PS_HASH_LEN]; - u8 query_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN]; - u8 query_count; u8 p2ps_seek; + u8 p2ps_seek_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN]; u8 p2ps_seek_count; - u8 p2ps_svc_found; #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie_beacon; @@ -529,6 +538,9 @@ struct p2p_data { u16 authorized_oob_dev_pw_id; struct wpabuf **vendor_elem; + + unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS]; + unsigned int num_pref_freq; }; /** @@ -578,7 +590,7 @@ struct p2p_message { const u8 *p2p_device_addr; const u8 *pri_dev_type; u8 num_sec_dev_types; - char device_name[33]; + char device_name[WPS_DEV_NAME_MAX_LEN + 1]; u16 config_methods; /* WPS IE */ @@ -631,6 +643,9 @@ struct p2p_message { const u8 *persistent_dev; const u8 *persistent_ssid; size_t persistent_ssid_len; + + const u8 *pref_freq_list; + size_t pref_freq_list_len; }; @@ -757,6 +772,8 @@ void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr, const u8 *ssid, size_t ssid_len); int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, int all_attr); +void p2p_buf_add_pref_channel_list(struct wpabuf *buf, + const u32 *preferred_freq_list, u32 size); /* p2p_sd.c */ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, @@ -786,6 +803,8 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev); u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method); void p2p_reselect_channel(struct p2p_data *p2p, struct p2p_channels *intersection); +void p2p_check_pref_chan(struct p2p_data *p2p, int go, + struct p2p_device *dev, struct p2p_message *msg); /* p2p_pd.c */ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, @@ -795,6 +814,7 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, int join, int force_freq); void p2p_reset_pending_pd(struct p2p_data *p2p); +void p2ps_prov_free(struct p2p_data *p2p); /* p2p_invitation.c */ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, @@ -840,7 +860,9 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer); int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps); int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[], size_t num_req_dev_type); -struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p); +struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p, + const u8 *query_hash, + u8 query_count); void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len); int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c index 558c6dd0c58fa..108e5b7f93e4d 100644 --- a/src/p2p/p2p_invitation.c +++ b/src/p2p/p2p_invitation.c @@ -85,6 +85,9 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, p2p_buf_add_device_info(buf, p2p, peer); p2p_buf_update_ie_hdr(buf, len); + p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list, + p2p->num_pref_freq); + #ifdef CONFIG_WIFI_DISPLAY if (wfd_ie) wpabuf_put_buf(buf, wfd_ie); @@ -134,6 +137,9 @@ static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, extra = wpabuf_len(wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]) + extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -158,6 +164,9 @@ static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, wpabuf_put_buf(buf, wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]) + wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]); + return buf; } @@ -337,6 +346,12 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, p2p_reselect_channel(p2p, &intersection); } + /* + * Use the driver preferred frequency list extension if + * supported. + */ + p2p_check_pref_chan(p2p, go, dev, &msg); + op_freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); if (op_freq < 0) { @@ -387,7 +402,7 @@ fail: } else p2p->inv_group_bssid_ptr = NULL; if (msg.group_id) { - if (msg.group_id_len - ETH_ALEN <= 32) { + if (msg.group_id_len - ETH_ALEN <= SSID_MAX_LEN) { os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN); p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN; @@ -528,6 +543,12 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, peer_oper_freq = 0; } + /* + * Use the driver preferred frequency list extension if + * supported. + */ + p2p_check_pref_chan(p2p, 0, dev, &msg); + p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status, msg.group_bssid, channels, sa, freq, peer_oper_freq); diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c index fd6a4610d839f..bd1e68bd42417 100644 --- a/src/p2p/p2p_parse.c +++ b/src/p2p/p2p_parse.c @@ -149,7 +149,8 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, pos += 2; nlen = WPA_GET_BE16(pos); pos += 2; - if (data + len - pos < (int) nlen || nlen > 32) { + if (data + len - pos < (int) nlen || + nlen > WPS_DEV_NAME_MAX_LEN) { wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name " "length %d (buf len %d)", (int) nlen, (int) (data + len - pos)); @@ -160,8 +161,7 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, for (i = 0; i < nlen; i++) { if (msg->device_name[i] == '\0') break; - if (msg->device_name[i] > 0 && - msg->device_name[i] < 32) + if (is_ctrl_char(msg->device_name[i])) msg->device_name[i] = '_'; } wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR @@ -203,7 +203,7 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, MAC2STR(msg->group_bssid)); break; case P2P_ATTR_GROUP_ID: - if (len < ETH_ALEN || len > ETH_ALEN + 32) { + if (len < ETH_ALEN || len > ETH_ALEN + SSID_MAX_LEN) { wpa_printf(MSG_DEBUG, "P2P: Invalid P2P Group ID " "attribute length %d", len); return -1; @@ -371,9 +371,9 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, break; case P2P_ATTR_PERSISTENT_GROUP: { - if (len < ETH_ALEN) { + if (len < ETH_ALEN || len > ETH_ALEN + SSID_MAX_LEN) { wpa_printf(MSG_DEBUG, - "P2P: Too short Persistent Group Info (length %u)", + "P2P: Invalid Persistent Group Info (length %u)", len); return -1; } @@ -516,7 +516,7 @@ int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg) struct ieee802_11_elems elems; ieee802_11_parse_elems(data, len, &elems, 0); - if (elems.ds_params && elems.ds_params_len >= 1) + if (elems.ds_params) msg->ds_params = elems.ds_params; if (elems.ssid) msg->ssid = elems.ssid - 2; @@ -548,6 +548,9 @@ int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg) } #endif /* CONFIG_WIFI_DISPLAY */ + msg->pref_freq_list = elems.pref_freq_list; + msg->pref_freq_list_len = elems.pref_freq_list_len; + return 0; } @@ -674,8 +677,8 @@ int p2p_group_info_parse(const u8 *gi, size_t gi_len, t += 2; if (count > cend - t) return -1; /* invalid Device Name TLV */ - if (count >= 32) - count = 32; + if (count >= WPS_DEV_NAME_MAX_LEN) + count = WPS_DEV_NAME_MAX_LEN; cli->dev_name = (const char *) t; cli->dev_name_len = count; @@ -703,7 +706,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, for (i = 0; i < info.num_clients; i++) { struct p2p_client_info *cli; - char name[33]; + char name[WPS_DEV_NAME_MAX_LEN + 1]; char devtype[WPS_DEV_TYPE_BUFSIZE]; u8 s; int count; @@ -742,7 +745,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, name[cli->dev_name_len] = '\0'; count = (int) cli->dev_name_len - 1; while (count >= 0) { - if (name[count] > 0 && name[count] < 32) + if (is_ctrl_char(name[count])) name[count] = '_'; count--; } diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c index 328b1e029ce57..890094551821a 100644 --- a/src/p2p/p2p_pd.c +++ b/src/p2p/p2p_pd.c @@ -44,7 +44,7 @@ static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf) { int found; u8 intended_addr[ETH_ALEN]; - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; int group_iface; @@ -57,7 +57,11 @@ static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf) if (found) { p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, ssid, ssid_len); - p2p_buf_add_intended_addr(buf, intended_addr); + + if (group_iface) + p2p_buf_add_intended_addr(buf, p2p->intended_addr); + else + p2p_buf_add_intended_addr(buf, intended_addr); } else { if (!p2p->ssid_set) { p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len); @@ -82,11 +86,12 @@ static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, struct wpabuf *buf, u16 config_methods) { struct p2ps_provision *prov = p2p->p2ps_prov; - u8 feat_cap_mask[] = { 1, 0 }; + struct p2ps_feature_capab fcap = { prov->cpt_mask, 0 }; int shared_group = 0; - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u8 go_dev_addr[ETH_ALEN]; + u8 intended_addr[ETH_ALEN]; /* If we might be explicite group owner, add GO details */ if (prov->conncap & (P2PS_SETUP_GROUP_OWNER | @@ -101,7 +106,7 @@ static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, if (p2p->cfg->get_persistent_group) { shared_group = p2p->cfg->get_persistent_group( p2p->cfg->cb_ctx, dev->info.p2p_device_addr, NULL, 0, - go_dev_addr, ssid, &ssid_len); + go_dev_addr, ssid, &ssid_len, intended_addr); } /* Add Operating Channel if conncap includes GO */ @@ -146,12 +151,17 @@ static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac); - p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask), - feat_cap_mask); + p2p_buf_add_feature_capability(buf, sizeof(fcap), (const u8 *) &fcap); - if (shared_group) + if (shared_group) { p2p_buf_add_persistent_group_info(buf, go_dev_addr, ssid, ssid_len); + /* Add intended interface address if it is not added yet */ + if ((prov->conncap == P2PS_SETUP_NONE || + prov->conncap == P2PS_SETUP_CLIENT) && + !is_zero_ether_addr(intended_addr)) + p2p_buf_add_intended_addr(buf, intended_addr); + } } @@ -232,7 +242,9 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, const u8 *group_id, size_t group_id_len, const u8 *persist_ssid, - size_t persist_ssid_len) + size_t persist_ssid_len, + const u8 *fcap, + u16 fcap_len) { struct wpabuf *buf; size_t extra = 0; @@ -270,7 +282,6 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, /* Add P2P IE for P2PS */ if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) { - u8 feat_cap_mask[] = { 1, 0 }; u8 *len = p2p_buf_add_ie_hdr(buf); struct p2ps_provision *prov = p2p->p2ps_prov; u8 group_capab; @@ -293,18 +304,23 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, if (persist_ssid && p2p->cfg->get_persistent_group && (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED)) { - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u8 go_dev_addr[ETH_ALEN]; + u8 intended_addr[ETH_ALEN]; persist = p2p->cfg->get_persistent_group( p2p->cfg->cb_ctx, dev->info.p2p_device_addr, persist_ssid, persist_ssid_len, go_dev_addr, - ssid, &ssid_len); - if (persist) + ssid, &ssid_len, intended_addr); + if (persist) { p2p_buf_add_persistent_group_info( buf, go_dev_addr, ssid, ssid_len); + if (!is_zero_ether_addr(intended_addr)) + p2p_buf_add_intended_addr( + buf, intended_addr); + } } if (!persist && (prov->conncap & P2PS_SETUP_GROUP_OWNER)) @@ -344,8 +360,7 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac); - p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask), - feat_cap_mask); + p2p_buf_add_feature_capability(buf, fcap_len, fcap); p2p_buf_update_ie_hdr(buf, len); } else if (status != P2P_SC_SUCCESS || adv_id) { u8 *len = p2p_buf_add_ie_hdr(buf); @@ -400,6 +415,18 @@ static int p2ps_setup_p2ps_prov(struct p2p_data *p2p, u32 adv_id, } +static u8 p2ps_own_preferred_cpt(const u8 *cpt_priority, u8 req_cpt_mask) +{ + int i; + + for (i = 0; cpt_priority[i]; i++) + if (req_cpt_mask & cpt_priority[i]) + return cpt_priority[i]; + + return 0; +} + + void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { @@ -415,9 +442,12 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, u32 session_id = 0; u8 session_mac[ETH_ALEN]; u8 adv_mac[ETH_ALEN]; - u8 group_mac[ETH_ALEN]; + const u8 *group_mac; int passwd_id = DEV_PW_DEFAULT; u16 config_methods; + u16 allowed_config_methods = WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; + struct p2ps_feature_capab resp_fcap = { 0, 0 }; + struct p2ps_feature_capab *req_fcap; if (p2p_parse(data, len, &msg)) return; @@ -425,6 +455,7 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR " with config methods 0x%x (freq=%d)", MAC2STR(sa), msg.wps_config_methods, rx_freq); + group_mac = msg.intended_addr; dev = p2p_get_device(p2p, sa); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { @@ -441,9 +472,12 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); } - if (!(msg.wps_config_methods & - (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | - WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_P2PS))) { + if (msg.adv_id) + allowed_config_methods |= WPS_CONFIG_P2PS; + else + allowed_config_methods |= WPS_CONFIG_PUSHBUTTON; + + if (!(msg.wps_config_methods & allowed_config_methods)) { p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request"); goto out; } @@ -501,14 +535,23 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, os_memset(session_mac, 0, ETH_ALEN); os_memset(adv_mac, 0, ETH_ALEN); - os_memset(group_mac, 0, ETH_ALEN); + /* Note 1: A feature capability attribute structure can be changed + * in the future. The assumption is that such modifications are + * backwards compatible, therefore we allow processing of + * msg.feature_cap exceeding the size of the p2ps_feature_capab + * structure. + * Note 2: Vverification of msg.feature_cap_len below has to be changed + * to allow 2 byte feature capability processing if struct + * p2ps_feature_capab is extended to include additional fields and it + * affects the structure size. + */ if (msg.adv_id && msg.session_id && msg.session_mac && msg.adv_mac && + msg.feature_cap && msg.feature_cap_len >= sizeof(*req_fcap) && (msg.status || msg.conn_cap)) { u8 remote_conncap; - if (msg.intended_addr) - os_memcpy(group_mac, msg.intended_addr, ETH_ALEN); + req_fcap = (struct p2ps_feature_capab *) msg.feature_cap; os_memcpy(session_mac, msg.session_mac, ETH_ALEN); os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); @@ -533,9 +576,22 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d", auto_accept, remote_conncap, conncap); - if (p2ps_adv->config_methods && - !(msg.wps_config_methods & - p2ps_adv->config_methods)) { + resp_fcap.cpt = + p2ps_own_preferred_cpt(p2ps_adv->cpt_priority, + req_fcap->cpt); + + p2p_dbg(p2p, + "cpt: service:0x%x remote:0x%x result:0x%x", + p2ps_adv->cpt_mask, req_fcap->cpt, + resp_fcap.cpt); + + if (!resp_fcap.cpt) { + p2p_dbg(p2p, + "Incompatible P2PS feature capability CPT bitmask"); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else if (p2ps_adv->config_methods && + !(msg.wps_config_methods & + p2ps_adv->config_methods)) { p2p_dbg(p2p, "Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)", p2ps_adv->config_methods, @@ -624,6 +680,15 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, p2p->p2ps_prov->conncap, remote_conncap, conncap); + resp_fcap.cpt = p2ps_own_preferred_cpt( + p2p->p2ps_prov->cpt_priority, + req_fcap->cpt); + + p2p_dbg(p2p, + "cpt: local:0x%x remote:0x%x result:0x%x", + p2p->p2ps_prov->cpt_mask, + req_fcap->cpt, resp_fcap.cpt); + /* * Ensure that if we asked for PIN originally, * our method is consistent with original @@ -634,14 +699,22 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, else if (method & WPS_CONFIG_KEYPAD) method = WPS_CONFIG_DISPLAY; - /* Reject this "Deferred Accept* if incompatible - * conncap or method */ if (!conncap || - !(msg.wps_config_methods & method)) + !(msg.wps_config_methods & method)) { + /* + * Reject this "Deferred Accept* + * if incompatible conncap or method + */ + reject = + P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else if (!resp_fcap.cpt) { + p2p_dbg(p2p, + "Incompatible P2PS feature capability CPT bitmask"); reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - else + } else { reject = P2P_SC_SUCCESS; + } p2p->p2ps_prov->status = reject; p2p->p2ps_prov->conncap = conncap; @@ -659,7 +732,9 @@ out: config_methods, adv_id, msg.group_id, msg.group_id_len, msg.persistent_ssid, - msg.persistent_ssid_len); + msg.persistent_ssid_len, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); if (resp == NULL) { p2p_parse_free(&msg); return; @@ -696,7 +771,7 @@ out: NULL, adv_id, session_id, 0, 0, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 0, NULL); + 0, 0, NULL, NULL, 0); } else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { p2p->p2ps_prov->status = reject; @@ -709,7 +784,7 @@ out: session_id, conncap, 0, msg.persistent_ssid, msg.persistent_ssid_len, 0, - 0, NULL); + 0, NULL, NULL, 0); else p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, @@ -719,7 +794,9 @@ out: passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, 0, - 0, NULL); + 0, NULL, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); } else if (msg.status && p2p->p2ps_prov) { p2p->p2ps_prov->status = P2P_SC_SUCCESS; p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa, @@ -728,7 +805,9 @@ out: passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 0, NULL); + 0, 0, NULL, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); } else if (msg.status) { } else if (auto_accept && reject == P2P_SC_SUCCESS) { p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS, @@ -737,7 +816,9 @@ out: conncap, passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 0, NULL); + 0, 0, NULL, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && (!msg.session_info || !msg.session_info_len)) { p2p->p2ps_prov->method = msg.wps_config_methods; @@ -748,7 +829,9 @@ out: conncap, passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 1, NULL); + 0, 1, NULL, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { size_t buf_len = msg.session_info_len; char *buf = os_malloc(2 * buf_len + 1); @@ -764,14 +847,45 @@ out: adv_mac, session_mac, group_mac, adv_id, session_id, conncap, passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 1, buf); + 0, 1, buf, + (const u8 *) &resp_fcap, sizeof(resp_fcap)); os_free(buf); } } - if (reject == P2P_SC_SUCCESS && p2p->cfg->prov_disc_req) { + /* + * prov_disc_req callback is used to generate P2P-PROV-DISC-ENTER-PIN, + * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events. + * Call it either on legacy P2P PD or on P2PS PD only if we need to + * enter/show PIN. + * + * The callback is called in the following cases: + * 1. Legacy P2P PD request, response status SUCCESS + * 2. P2PS advertiser, method: DISPLAY, autoaccept: TRUE, + * response status: SUCCESS + * 3. P2PS advertiser, method DISPLAY, autoaccept: FALSE, + * response status: INFO_CURRENTLY_UNAVAILABLE + * 4. P2PS advertiser, method: KEYPAD, autoaccept==any, + * response status: INFO_CURRENTLY_UNAVAILABLE + * 5. P2PS follow-on with SUCCESS_DEFERRED, + * advertiser role: DISPLAY, autoaccept: FALSE, + * seeker: KEYPAD, response status: SUCCESS + */ + if (p2p->cfg->prov_disc_req && + ((reject == P2P_SC_SUCCESS && !msg.adv_id) || + (!msg.status && + (reject == P2P_SC_SUCCESS || + reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) && + passwd_id == DEV_PW_USER_SPECIFIED) || + (!msg.status && + reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && + passwd_id == DEV_PW_REGISTRAR_SPECIFIED) || + (reject == P2P_SC_SUCCESS && + msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && + passwd_id == DEV_PW_REGISTRAR_SPECIFIED))) { const u8 *dev_addr = sa; + if (msg.p2p_device_addr) dev_addr = msg.p2p_device_addr; p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa, @@ -783,10 +897,133 @@ out: 0, msg.group_id, msg.group_id_len); } + + if (dev && reject == P2P_SC_SUCCESS) { + switch (config_methods) { + case WPS_CONFIG_DISPLAY: + dev->wps_prov_info = WPS_CONFIG_KEYPAD; + break; + case WPS_CONFIG_KEYPAD: + dev->wps_prov_info = WPS_CONFIG_DISPLAY; + break; + case WPS_CONFIG_PUSHBUTTON: + dev->wps_prov_info = WPS_CONFIG_PUSHBUTTON; + break; + case WPS_CONFIG_P2PS: + dev->wps_prov_info = WPS_CONFIG_P2PS; + break; + default: + dev->wps_prov_info = 0; + break; + } + + if (msg.intended_addr) + os_memcpy(dev->interface_addr, msg.intended_addr, + ETH_ALEN); + } p2p_parse_free(&msg); } +static int p2p_validate_p2ps_pd_resp(struct p2p_data *p2p, + struct p2p_message *msg) +{ + u8 conn_cap_go = 0; + u8 conn_cap_cli = 0; + u32 session_id; + u32 adv_id; + +#define P2PS_PD_RESP_CHECK(_val, _attr) \ + do { \ + if ((_val) && !msg->_attr) { \ + p2p_dbg(p2p, "P2PS PD Response missing " #_attr); \ + return -1; \ + } \ + } while (0) + + P2PS_PD_RESP_CHECK(1, status); + P2PS_PD_RESP_CHECK(1, adv_id); + P2PS_PD_RESP_CHECK(1, adv_mac); + P2PS_PD_RESP_CHECK(1, capability); + P2PS_PD_RESP_CHECK(1, p2p_device_info); + P2PS_PD_RESP_CHECK(1, session_id); + P2PS_PD_RESP_CHECK(1, session_mac); + P2PS_PD_RESP_CHECK(1, feature_cap); + + session_id = WPA_GET_LE32(msg->session_id); + adv_id = WPA_GET_LE32(msg->adv_id); + + if (p2p->p2ps_prov->session_id != session_id) { + p2p_dbg(p2p, + "Ignore PD Response with unexpected Session ID"); + return -1; + } + + if (os_memcmp(p2p->p2ps_prov->session_mac, msg->session_mac, + ETH_ALEN)) { + p2p_dbg(p2p, + "Ignore PD Response with unexpected Session MAC"); + return -1; + } + + if (p2p->p2ps_prov->adv_id != adv_id) { + p2p_dbg(p2p, + "Ignore PD Response with unexpected Advertisement ID"); + return -1; + } + + if (os_memcmp(p2p->p2ps_prov->adv_mac, msg->adv_mac, ETH_ALEN) != 0) { + p2p_dbg(p2p, + "Ignore PD Response with unexpected Advertisement MAC"); + return -1; + } + + if (msg->listen_channel) { + p2p_dbg(p2p, + "Ignore malformed PD Response - unexpected Listen Channel"); + return -1; + } + + if (*msg->status == P2P_SC_SUCCESS && + !(!!msg->conn_cap ^ !!msg->persistent_dev)) { + p2p_dbg(p2p, + "Ignore malformed PD Response - either conn_cap or persistent group should be present"); + return -1; + } + + if (msg->persistent_dev && *msg->status != P2P_SC_SUCCESS) { + p2p_dbg(p2p, + "Ignore malformed PD Response - persistent group is present, but the status isn't success"); + return -1; + } + + if (msg->conn_cap) { + conn_cap_go = *msg->conn_cap == P2PS_SETUP_GROUP_OWNER; + conn_cap_cli = *msg->conn_cap == P2PS_SETUP_CLIENT; + } + + P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli, + channel_list); + P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli, + config_timeout); + + P2PS_PD_RESP_CHECK(conn_cap_go, group_id); + P2PS_PD_RESP_CHECK(conn_cap_go, intended_addr); + P2PS_PD_RESP_CHECK(conn_cap_go, operating_channel); + /* + * TODO: Also validate that operating channel is present if the device + * is a GO in a persistent group. We can't do it here since we don't + * know what is the role of the peer. It should be probably done in + * p2ps_prov_complete callback, but currently operating channel isn't + * passed to it. + */ + +#undef P2PS_PD_RESP_CHECK + + return 0; +} + + void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len) { @@ -794,24 +1031,26 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, struct p2p_device *dev; u16 report_config_methods = 0, req_config_methods; u8 status = P2P_SC_SUCCESS; - int success = 0; u32 adv_id = 0; u8 conncap = P2PS_SETUP_NEW; u8 adv_mac[ETH_ALEN]; - u8 group_mac[ETH_ALEN]; + const u8 *group_mac; int passwd_id = DEV_PW_DEFAULT; + int p2ps_seeker; if (p2p_parse(data, len, &msg)) return; + if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, &msg)) { + p2p_parse_free(&msg); + return; + } + /* Parse the P2PS members present */ if (msg.status) status = *msg.status; - if (msg.intended_addr) - os_memcpy(group_mac, msg.intended_addr, ETH_ALEN); - else - os_memset(group_mac, 0, ETH_ALEN); + group_mac = msg.intended_addr; if (msg.adv_mac) os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); @@ -859,6 +1098,8 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, p2p->pending_action_state = P2P_NO_PENDING_ACTION; } + p2ps_seeker = p2p->p2ps_prov && p2p->p2ps_prov->pd_seeker; + /* * Use a local copy of the requested config methods since * p2p_reset_pending_pd() can clear this in the peer entry. @@ -881,8 +1122,7 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, P2P_PROV_DISC_REJECTED, adv_id, adv_mac, NULL); p2p_parse_free(&msg); - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; + p2ps_prov_free(p2p); goto out; } @@ -909,7 +1149,6 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, } if ((msg.conn_cap || msg.persistent_dev) && - msg.adv_id && (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) && p2p->p2ps_prov) { if (p2p->cfg->p2ps_prov_complete) { @@ -918,23 +1157,20 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, p2p->p2ps_prov->session_mac, group_mac, adv_id, p2p->p2ps_prov->session_id, conncap, passwd_id, msg.persistent_ssid, - msg.persistent_ssid_len, 1, 0, NULL); + msg.persistent_ssid_len, 1, 0, NULL, + msg.feature_cap, msg.feature_cap_len); } - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; - } - - if (status != P2P_SC_SUCCESS && - status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && - status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { + p2ps_prov_free(p2p); + } else if (status != P2P_SC_SUCCESS && + status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && + status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { if (p2p->cfg->p2ps_prov_complete) p2p->cfg->p2ps_prov_complete( p2p->cfg->cb_ctx, status, sa, adv_mac, p2p->p2ps_prov->session_mac, group_mac, adv_id, p2p->p2ps_prov->session_id, - 0, 0, NULL, 0, 1, 0, NULL); - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; + 0, 0, NULL, 0, 1, 0, NULL, NULL, 0); + p2ps_prov_free(p2p); } if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { @@ -950,8 +1186,7 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, if (!deferred_sess_resp) { p2p_parse_free(&msg); - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; + p2ps_prov_free(p2p); goto out; } utf8_escape((char *) msg.session_info, info_len, @@ -970,24 +1205,23 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, p2p->cfg->cb_ctx, sa, P2P_PROV_DISC_INFO_UNAVAILABLE, adv_id, adv_mac, NULL); - } else if (msg.wps_config_methods != dev->req_config_methods || - status != P2P_SC_SUCCESS) { + } else if (status != P2P_SC_SUCCESS) { p2p_dbg(p2p, "Peer rejected our Provision Discovery Request"); if (p2p->cfg->prov_disc_fail) p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, - P2P_PROV_DISC_REJECTED, 0, - NULL, NULL); + P2P_PROV_DISC_REJECTED, + adv_id, adv_mac, NULL); p2p_parse_free(&msg); - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; + p2ps_prov_free(p2p); goto out; } /* Store the provisioning info */ dev->wps_prov_info = msg.wps_config_methods; + if (msg.intended_addr) + os_memcpy(dev->interface_addr, msg.intended_addr, ETH_ALEN); p2p_parse_free(&msg); - success = 1; out: dev->req_config_methods = 0; @@ -999,7 +1233,28 @@ out: p2p_connect_send(p2p, dev); return; } - if (success && p2p->cfg->prov_disc_resp) + + /* + * prov_disc_resp callback is used to generate P2P-PROV-DISC-ENTER-PIN, + * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events. + * Call it only for a legacy P2P PD or for P2PS PD scenarios where + * show/enter PIN events are needed. + * + * The callback is called in the following cases: + * 1. Legacy P2P PD response with a status SUCCESS + * 2. P2PS, advertiser method: DISPLAY, autoaccept: true, + * response status: SUCCESS, local method KEYPAD + * 3. P2PS, advertiser method: KEYPAD,Seeker side, + * response status: INFO_CURRENTLY_UNAVAILABLE, + * local method: DISPLAY + */ + if (p2p->cfg->prov_disc_resp && + ((status == P2P_SC_SUCCESS && !adv_id) || + (p2ps_seeker && status == P2P_SC_SUCCESS && + passwd_id == DEV_PW_REGISTRAR_SPECIFIED) || + (p2ps_seeker && + status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && + passwd_id == DEV_PW_USER_SPECIFIED))) p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa, report_config_methods); @@ -1120,7 +1375,7 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, /* Reset provisioning info */ dev->wps_prov_info = 0; - os_free(p2p->p2ps_prov); + p2ps_prov_free(p2p); p2p->p2ps_prov = p2ps_prov; dev->req_config_methods = config_methods; @@ -1176,3 +1431,10 @@ void p2p_reset_pending_pd(struct p2p_data *p2p) p2p->pd_retries = 0; p2p->pd_force_freq = 0; } + + +void p2ps_prov_free(struct p2p_data *p2p) +{ + os_free(p2p->p2ps_prov); + p2p->p2ps_prov = NULL; +} diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c index f32751d79ca8b..2e2aa8ad06f08 100644 --- a/src/p2p/p2p_utils.c +++ b/src/p2p/p2p_utils.c @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "common/defs.h" #include "common/ieee802_11_common.h" #include "p2p_i.h" @@ -67,50 +68,11 @@ int p2p_channel_to_freq(int op_class, int channel) */ int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel) { - /* TODO: more operating classes */ - if (freq >= 2412 && freq <= 2472) { - if ((freq - 2407) % 5) - return -1; - - *op_class = 81; /* 2.407 GHz, channels 1..13 */ - *channel = (freq - 2407) / 5; - return 0; - } - - if (freq == 2484) { - *op_class = 82; /* channel 14 */ - *channel = 14; - return 0; - } - - if (freq >= 5180 && freq <= 5240) { - if ((freq - 5000) % 5) - return -1; - - *op_class = 115; /* 5 GHz, channels 36..48 */ - *channel = (freq - 5000) / 5; - return 0; - } - - if (freq >= 5745 && freq <= 5805) { - if ((freq - 5000) % 5) - return -1; - - *op_class = 124; /* 5 GHz, channels 149..161 */ - *channel = (freq - 5000) / 5; - return 0; - } - - if (freq >= 58320 && freq <= 64800) { - if ((freq - 58320) % 2160) - return -1; - - *op_class = 180; /* 60 GHz, channels 1..4 */ - *channel = (freq - 56160) / 2160; - return 0; - } + if (ieee80211_freq_to_channel_ext(freq, 0, 0, op_class, channel) == + NUM_HOSTAPD_MODES) + return -1; - return -1; + return 0; } @@ -497,12 +459,22 @@ int p2p_channels_to_freqs(const struct p2p_channels *channels, int *freq_list, break; for (j = 0; j < c->channels; j++) { int freq; + unsigned int k; + if (idx + 1 == max_len) break; freq = p2p_channel_to_freq(c->reg_class, c->channel[j]); if (freq < 0) continue; + + for (k = 0; k < idx; k++) { + if (freq_list[k] == freq) + break; + } + + if (k < idx) + continue; freq_list[idx++] = freq; } } diff --git a/src/radius/Makefile b/src/radius/Makefile index b5d063dac10d6..3ad4751dfbfe5 100644 --- a/src/radius/Makefile +++ b/src/radius/Makefile @@ -14,6 +14,7 @@ CFLAGS += -DCONFIG_IPV6 LIB_OBJS= \ radius.o \ radius_client.o \ + radius_das.o \ radius_server.o libradius.a: $(LIB_OBJS) diff --git a/src/radius/radius.c b/src/radius/radius.c index 8d878a4bd0783..1ebfd11f3b9a2 100644 --- a/src/radius/radius.c +++ b/src/radius/radius.c @@ -167,7 +167,7 @@ struct radius_attr_type { } data_type; }; -static struct radius_attr_type radius_attrs[] = +static const struct radius_attr_type radius_attrs[] = { { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST }, @@ -259,7 +259,7 @@ static struct radius_attr_type radius_attrs[] = #define RADIUS_ATTRS ARRAY_SIZE(radius_attrs) -static struct radius_attr_type *radius_get_attr_type(u8 type) +static const struct radius_attr_type *radius_get_attr_type(u8 type) { size_t i; @@ -274,7 +274,7 @@ static struct radius_attr_type *radius_get_attr_type(u8 type) static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) { - struct radius_attr_type *attr; + const struct radius_attr_type *attr; int len; unsigned char *pos; char buf[1000]; @@ -1225,7 +1225,7 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg, } /* MS-MPPE-Recv-Key */ - buf = os_malloc(hlen + send_key_len + 16); + buf = os_malloc(hlen + recv_key_len + 16); if (buf == NULL) { return 0; } @@ -1425,7 +1425,7 @@ struct radius_tunnel_attrs { /** * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information * @msg: RADIUS message - * Returns: VLAN ID for the first tunnel configuration of -1 if none is found + * Returns: VLAN ID for the first tunnel configuration or 0 if none is found */ int radius_msg_get_vlanid(struct radius_msg *msg) { @@ -1488,7 +1488,7 @@ int radius_msg_get_vlanid(struct radius_msg *msg) return tun->vlanid; } - return -1; + return 0; } diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c index 39ceea879cafe..b7d991bbd0979 100644 --- a/src/radius/radius_das.c +++ b/src/radius/radius_das.c @@ -245,7 +245,7 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) (u8 *) &val, 4); if (res == 4) { u32 timestamp = ntohl(val); - if ((unsigned int) abs(now.sec - timestamp) > + if ((unsigned int) abs((int) (now.sec - timestamp)) > das->time_window) { wpa_printf(MSG_DEBUG, "DAS: Unacceptable " "Event-Timestamp (%u; local time %u) in " diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index 85a485e91d932..744283c7dc9d7 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -35,7 +35,7 @@ */ #define RADIUS_MAX_MSG_LEN 3000 -static struct eapol_callbacks radius_server_eapol_cb; +static const struct eapol_callbacks radius_server_eapol_cb; struct radius_client; struct radius_server_data; @@ -265,6 +265,8 @@ struct radius_server_data { struct dl_list erp_keys; /* struct eap_server_erp_key */ + unsigned int tls_session_lifetime; + /** * wps - Wi-Fi Protected Setup context * @@ -688,6 +690,7 @@ radius_server_get_new_session(struct radius_server_data *data, eap_conf.server_id = (const u8 *) data->server_id; eap_conf.server_id_len = os_strlen(data->server_id); eap_conf.erp = data->erp; + eap_conf.tls_session_lifetime = data->tls_session_lifetime; radius_server_testing_options(sess, &eap_conf); sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb, &eap_conf); @@ -1711,8 +1714,10 @@ radius_server_init(struct radius_server_conf *conf) data->ipv6 = conf->ipv6; if (conf->pac_opaque_encr_key) { data->pac_opaque_encr_key = os_malloc(16); - os_memcpy(data->pac_opaque_encr_key, conf->pac_opaque_encr_key, - 16); + if (data->pac_opaque_encr_key) { + os_memcpy(data->pac_opaque_encr_key, + conf->pac_opaque_encr_key, 16); + } } if (conf->eap_fast_a_id) { data->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len); @@ -1743,6 +1748,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; if (conf->subscr_remediation_url) { data->subscr_remediation_url = @@ -2035,6 +2041,12 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity, sess->remediation = user->remediation; sess->macacl = user->macacl; } + + if (ret) { + RADIUS_DEBUG("%s: User-Name not found from user database", + __func__); + } + return ret; } @@ -2095,7 +2107,7 @@ static int radius_server_erp_add_key(void *ctx, struct eap_server_erp_key *erp) #endif /* CONFIG_ERP */ -static struct eapol_callbacks radius_server_eapol_cb = +static const struct eapol_callbacks radius_server_eapol_cb = { .get_eap_user = radius_server_get_eap_user, .get_eap_req_id_text = radius_server_get_eap_req_id_text, diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h index ca4e38c12e995..7a25802c81527 100644 --- a/src/radius/radius_server.h +++ b/src/radius/radius_server.h @@ -170,6 +170,8 @@ struct radius_server_conf { const char *erp_domain; + unsigned int tls_session_lifetime; + /** * wps - Wi-Fi Protected Setup context * diff --git a/src/rsn_supp/Makefile b/src/rsn_supp/Makefile index adfd3dfd5b9be..d5e61fe72dc8f 100644 --- a/src/rsn_supp/Makefile +++ b/src/rsn_supp/Makefile @@ -1,8 +1,30 @@ -all: - @echo Nothing to be made. +all: librsn_supp.a clean: - rm -f *~ *.o *.d *.gcno *.gcda *.gcov + rm -f *~ *.o *.d *.gcno *.gcda *.gcov librsn_supp.a install: @echo Nothing to be made. + +include ../lib.rules + +CFLAGS += -DCONFIG_IEEE80211W +CFLAGS += -DCONFIG_IEEE80211R +CFLAGS += -DCONFIG_PEERKEY +CFLAGS += -DCONFIG_TDLS +CFLAGS += -DCONFIG_WNM +CFLAGS += -DIEEE8021X_EAPOL + +LIB_OBJS= \ + pmksa_cache.o \ + wpa_ft.o \ + peerkey.o \ + tdls.o \ + preauth.o \ + wpa.o \ + wpa_ie.o + +librsn_supp.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c index c1d7749191d75..722c20a706f9d 100644 --- a/src/rsn_supp/tdls.c +++ b/src/rsn_supp/tdls.c @@ -12,6 +12,7 @@ #include "utils/eloop.h" #include "utils/os.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" #include "crypto/sha256.h" #include "crypto/crypto.h" #include "crypto/aes_wrap.h" @@ -1577,9 +1578,7 @@ static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde, static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { - if (!kde->ht_capabilities || - kde->ht_capabilities_len < - sizeof(struct ieee80211_ht_capabilities) ) { + if (!kde->ht_capabilities) { wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities " "received"); return 0; @@ -1605,9 +1604,7 @@ static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde, static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { - if (!kde->vht_capabilities || - kde->vht_capabilities_len < - sizeof(struct ieee80211_vht_capabilities) ) { + if (!kde->vht_capabilities) { wpa_printf(MSG_DEBUG, "TDLS: No supported vht capabilities " "received"); return 0; @@ -2863,14 +2860,14 @@ void wpa_tdls_disassoc(struct wpa_sm *sm) } -static int wpa_tdls_prohibited(struct wpa_eapol_ie_parse *elems) +static int wpa_tdls_prohibited(struct ieee802_11_elems *elems) { /* bit 38 - TDLS Prohibited */ return !!(elems->ext_capab[2 + 4] & 0x40); } -static int wpa_tdls_chan_switch_prohibited(struct wpa_eapol_ie_parse *elems) +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); @@ -2879,12 +2876,13 @@ static int wpa_tdls_chan_switch_prohibited(struct wpa_eapol_ie_parse *elems) void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len) { - struct wpa_eapol_ie_parse elems; + struct ieee802_11_elems elems; sm->tdls_prohibited = 0; sm->tdls_chan_switch_prohibited = 0; - if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 || + if (ies == NULL || + ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed || elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) return; @@ -2900,9 +2898,10 @@ void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len) void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len) { - struct wpa_eapol_ie_parse elems; + struct ieee802_11_elems elems; - if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 || + if (ies == NULL || + ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed || elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) return; diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 8adeef4af1a5c..a9f255e37b436 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -249,6 +249,17 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, "RSN: the new PMK matches with the " "PMKID"); abort_cached = 0; + } else if (sa && !sm->cur_pmksa && pmkid) { + /* + * It looks like the authentication server + * derived mismatching MSK. This should not + * really happen, but bugs happen.. There is not + * much we can do here without knowing what + * exactly caused the server to misbehave. + */ + wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: PMKID mismatch - authentication server may have derived different MSK?!"); + return -1; } if (!sm->cur_pmksa) @@ -1281,8 +1292,8 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, &gd->key_rsc_len, &gd->alg)) return -1; - wpa_hexdump(MSG_DEBUG, "RSN: received GTK in group key handshake", - ie.gtk, ie.gtk_len); + wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in group key handshake", + ie.gtk, ie.gtk_len); gd->keyidx = ie.gtk[0] & 0x3; gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm, !!(ie.gtk[0] & BIT(2))); @@ -1333,6 +1344,11 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> WPA_KEY_INFO_KEY_INDEX_SHIFT; if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) { +#ifdef CONFIG_NO_RC4 + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: RC4 not supported in the build"); + return -1; +#else /* CONFIG_NO_RC4 */ u8 ek[32]; if (key_data_len > sizeof(gd->gtk)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -1350,6 +1366,7 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, return -1; } os_memset(ek, 0, sizeof(ek)); +#endif /* CONFIG_NO_RC4 */ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { if (maxkeylen % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -1564,6 +1581,11 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, /* Decrypt key data here so that this operation does not need * to be implemented separately for each message type. */ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) { +#ifdef CONFIG_NO_RC4 + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: RC4 not supported in the build"); + return -1; +#else /* CONFIG_NO_RC4 */ u8 ek[32]; os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len); @@ -1574,6 +1596,7 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, return -1; } os_memset(ek, 0, sizeof(ek)); +#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 || diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index 06dea0550f1bc..205793e7f43ab 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -168,9 +168,7 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, pos = (u8 *) (rsnie + 1); /* Group Suite Selector */ - if (sm->group_cipher != WPA_CIPHER_CCMP && - sm->group_cipher != WPA_CIPHER_GCMP && - sm->group_cipher != WPA_CIPHER_TKIP) { + if (!wpa_cipher_valid_group(sm->group_cipher)) { wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)", sm->group_cipher); os_free(buf); diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c index cb334df675beb..0c37b35c1ee1d 100644 --- a/src/rsn_supp/wpa_ie.c +++ b/src/rsn_supp/wpa_ie.c @@ -30,6 +30,9 @@ int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, { if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN) return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); + if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC && + wpa_ie[1] >= 4 && WPA_GET_BE32(&wpa_ie[2]) == OSEN_IE_VENDOR_TYPE) + return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); else return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data); } @@ -508,12 +511,14 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, ie->rsn_ie_len = pos[1] + 2; wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key", ie->rsn_ie, ie->rsn_ie_len); - } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { + } else if (*pos == WLAN_EID_MOBILITY_DOMAIN && + pos[1] >= sizeof(struct rsn_mdie)) { ie->mdie = pos; ie->mdie_len = pos[1] + 2; wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key", ie->mdie, ie->mdie_len); - } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) { + } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION && + pos[1] >= sizeof(struct rsn_ftie)) { ie->ftie = pos; ie->ftie_len = pos[1] + 2; wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key", @@ -548,15 +553,16 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, } else if (*pos == WLAN_EID_EXT_SUPP_RATES) { ie->ext_supp_rates = pos; ie->ext_supp_rates_len = pos[1] + 2; - } else if (*pos == WLAN_EID_HT_CAP) { + } else if (*pos == WLAN_EID_HT_CAP && + pos[1] >= sizeof(struct ieee80211_ht_capabilities)) { ie->ht_capabilities = pos + 2; - ie->ht_capabilities_len = pos[1]; } else if (*pos == WLAN_EID_VHT_AID) { if (pos[1] >= 2) ie->aid = WPA_GET_LE16(pos + 2) & 0x3fff; - } else if (*pos == WLAN_EID_VHT_CAP) { + } else if (*pos == WLAN_EID_VHT_CAP && + pos[1] >= sizeof(struct ieee80211_vht_capabilities)) + { ie->vht_capabilities = pos + 2; - ie->vht_capabilities_len = pos[1]; } else if (*pos == WLAN_EID_QOS && pos[1] >= 1) { ie->qosinfo = pos[2]; } else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) { diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h index 0fc42cc492ac5..fe95af0abc519 100644 --- a/src/rsn_supp/wpa_ie.h +++ b/src/rsn_supp/wpa_ie.h @@ -50,9 +50,7 @@ struct wpa_eapol_ie_parse { const u8 *ext_supp_rates; size_t ext_supp_rates_len; const u8 *ht_capabilities; - size_t ht_capabilities_len; const u8 *vht_capabilities; - size_t vht_capabilities_len; const u8 *supp_channels; size_t supp_channels_len; const u8 *supp_oper_classes; diff --git a/src/tls/libtommath.c b/src/tls/libtommath.c index 3fb8fbed25e78..8bc824f20dcdc 100644 --- a/src/tls/libtommath.c +++ b/src/tls/libtommath.c @@ -1472,8 +1472,7 @@ static int mp_init_multi(mp_int *mp, ...) cur_arg = va_arg(clean_args, mp_int*); } va_end(clean_args); - res = MP_MEM; - break; + return MP_MEM; } n++; cur_arg = va_arg(args, mp_int*); @@ -1631,7 +1630,7 @@ static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) } /* init our temps */ - if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL) != MP_OKAY)) { + if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) { return res; } diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c index facdd659173db..a6f0587e34c57 100644 --- a/src/tls/tlsv1_client.c +++ b/src/tls/tlsv1_client.c @@ -714,12 +714,12 @@ int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, /** - * tlsv1_client_get_keys - Get master key and random data from TLS connection + * tlsv1_client_get_random - Get random data from TLS connection * @conn: TLSv1 client connection data from tlsv1_client_init() - * @keys: Structure of key/random data (filled on success) + * @keys: Structure of random data (filled on success) * Returns: 0 on success, -1 on failure */ -int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys) +int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *keys) { os_memset(keys, 0, sizeof(*keys)); if (conn->state == CLIENT_HELLO) @@ -731,8 +731,6 @@ int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys) if (conn->state != SERVER_HELLO) { keys->server_random = conn->server_random; keys->server_random_len = TLS_RANDOM_LEN; - keys->master_key = conn->master_secret; - keys->master_key_len = TLS_MASTER_SECRET_LEN; } return 0; diff --git a/src/tls/tlsv1_client.h b/src/tls/tlsv1_client.h index 8ec85f1a91936..a4e25e969937c 100644 --- a/src/tls/tlsv1_client.h +++ b/src/tls/tlsv1_client.h @@ -36,7 +36,7 @@ int tlsv1_client_shutdown(struct tlsv1_client *conn); int tlsv1_client_resumed(struct tlsv1_client *conn); int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, const u8 *data, size_t data_len); -int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys); +int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *data); int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn); int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers); int tlsv1_client_set_cred(struct tlsv1_client *conn, diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c index 93ae4888d8980..ba47337bcbb11 100644 --- a/src/tls/tlsv1_server.c +++ b/src/tls/tlsv1_server.c @@ -610,12 +610,12 @@ int tlsv1_server_resumed(struct tlsv1_server *conn) /** - * tlsv1_server_get_keys - Get master key and random data from TLS connection + * tlsv1_server_get_random - Get random data from TLS connection * @conn: TLSv1 server connection data from tlsv1_server_init() - * @keys: Structure of key/random data (filled on success) + * @keys: Structure of random data (filled on success) * Returns: 0 on success, -1 on failure */ -int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys) +int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *keys) { os_memset(keys, 0, sizeof(*keys)); if (conn->state == CLIENT_HELLO) @@ -627,8 +627,6 @@ int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys) if (conn->state != SERVER_HELLO) { keys->server_random = conn->server_random; keys->server_random_len = TLS_RANDOM_LEN; - keys->master_key = conn->master_secret; - keys->master_key_len = TLS_MASTER_SECRET_LEN; } return 0; diff --git a/src/tls/tlsv1_server.h b/src/tls/tlsv1_server.h index b2b28d1e1215a..10e7699312b06 100644 --- a/src/tls/tlsv1_server.h +++ b/src/tls/tlsv1_server.h @@ -32,7 +32,7 @@ int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf, size_t buflen); int tlsv1_server_shutdown(struct tlsv1_server *conn); int tlsv1_server_resumed(struct tlsv1_server *conn); -int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys); +int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *data); int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn); int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers); int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer); diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c index 742af328cf8cc..b51dfcd447320 100644 --- a/src/tls/x509v3.c +++ b/src/tls/x509v3.c @@ -1511,7 +1511,7 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) if (pos + hdr.length < end) { wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER " "encoded certificate", - pos + hdr.length, end - pos + hdr.length); + pos + hdr.length, end - (pos + hdr.length)); end = pos + hdr.length; } diff --git a/src/utils/browser-wpadebug.c b/src/utils/browser-wpadebug.c index 5fc40fac610e9..59ba4d1e02d86 100644 --- a/src/utils/browser-wpadebug.c +++ b/src/utils/browser-wpadebug.c @@ -96,7 +96,7 @@ int hs20_web_browser(const char *url) if (pid == 0) { /* run the external command in the child process */ - char *argv[12]; + char *argv[14]; argv[0] = "browser-wpadebug"; argv[1] = "start"; @@ -109,7 +109,9 @@ int hs20_web_browser(const char *url) argv[8] = "-e"; argv[9] = "w1.fi.wpadebug.URL"; argv[10] = (void *) url; - argv[11] = NULL; + argv[11] = "--user"; + argv[12] = "-3"; /* USER_CURRENT_OR_SELF */ + argv[13] = NULL; execv("/system/bin/am", argv); wpa_printf(MSG_ERROR, "execv: %s", strerror(errno)); diff --git a/src/utils/common.c b/src/utils/common.c index 5fd795f3f3036..660e9fc985d69 100644 --- a/src/utils/common.c +++ b/src/utils/common.c @@ -8,6 +8,7 @@ #include "includes.h" +#include "common/ieee802_11_defs.h" #include "common.h" @@ -277,6 +278,31 @@ int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...) return ret; } + +int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len, + char sep) +{ + size_t i; + char *pos = buf, *end = buf + buf_size; + int ret; + + if (buf_size == 0) + return 0; + + for (i = 0; i < len; i++) { + ret = os_snprintf(pos, end - pos, "%02x%c", + data[i], sep); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return pos - buf; + } + pos += ret; + } + pos[-1] = '\0'; + return pos - buf; +} + + static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len, int uppercase) { @@ -584,7 +610,7 @@ size_t printf_decode(u8 *buf, size_t maxlen, const char *str) */ const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len) { - static char ssid_txt[32 * 4 + 1]; + static char ssid_txt[SSID_MAX_LEN * 4 + 1]; if (ssid == NULL) { ssid_txt[0] = '\0'; @@ -947,6 +973,48 @@ int random_mac_addr_keep_oui(u8 *addr) /** + * cstr_token - Get next token from const char string + * @str: a constant string to tokenize + * @delim: a string of delimiters + * @last: a pointer to a character following the returned token + * It has to be set to NULL for the first call and passed for any + * futher call. + * Returns: a pointer to token position in str or NULL + * + * This function is similar to str_token, but it can be used with both + * char and const char strings. Differences: + * - The str buffer remains unmodified + * - The returned token is not a NULL terminated string, but a token + * position in str buffer. If a return value is not NULL a size + * of the returned token could be calculated as (last - token). + */ +const char * cstr_token(const char *str, const char *delim, const char **last) +{ + const char *end, *token = str; + + if (!str || !delim || !last) + return NULL; + + if (*last) + token = *last; + + while (*token && os_strchr(delim, *token)) + token++; + + if (!*token) + return NULL; + + end = token + 1; + + while (*end && !os_strchr(delim, *end)) + end++; + + *last = end; + return token; +} + + +/** * str_token - Get next token from a string * @buf: String to tokenize. Note that the string might be modified. * @delim: String of delimiters @@ -956,25 +1024,12 @@ int random_mac_addr_keep_oui(u8 *addr) */ char * str_token(char *str, const char *delim, char **context) { - char *end, *pos = str; - - if (*context) - pos = *context; - - while (*pos && os_strchr(delim, *pos)) - pos++; - if (!*pos) - return NULL; - - end = pos + 1; - while (*end && !os_strchr(delim, *end)) - end++; + char *token = (char *) cstr_token(str, delim, (const char **) context); - if (*end) - *end++ = '\0'; + if (token && **context) + *(*context)++ = '\0'; - *context = end; - return pos; + return token; } @@ -1062,3 +1117,9 @@ size_t utf8_escape(const char *inp, size_t in_size, return res_size; } + + +int is_ctrl_char(char c) +{ + return c > 0 && c < 32; +} diff --git a/src/utils/common.h b/src/utils/common.h index 576e8e7e2d4eb..0b9cc3d882090 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -53,16 +53,6 @@ static inline unsigned int bswap_32(unsigned int v) } #endif /* __APPLE__ */ -#ifdef CONFIG_TI_COMPILER -#define __BIG_ENDIAN 4321 -#define __LITTLE_ENDIAN 1234 -#ifdef __big_endian__ -#define __BYTE_ORDER __BIG_ENDIAN -#else -#define __BYTE_ORDER __LITTLE_ENDIAN -#endif -#endif /* CONFIG_TI_COMPILER */ - #ifdef CONFIG_NATIVE_WINDOWS #include <winsock.h> @@ -110,22 +100,6 @@ typedef INT8 s8; #define WPA_TYPES_DEFINED #endif /* __vxworks */ -#ifdef CONFIG_TI_COMPILER -#ifdef _LLONG_AVAILABLE -typedef unsigned long long u64; -#else -/* - * TODO: 64-bit variable not available. Using long as a workaround to test the - * build, but this will likely not work for all operations. - */ -typedef unsigned long u64; -#endif -typedef unsigned int u32; -typedef unsigned short u16; -typedef unsigned char u8; -#define WPA_TYPES_DEFINED -#endif /* CONFIG_TI_COMPILER */ - #ifndef WPA_TYPES_DEFINED #ifdef CONFIG_USE_INTTYPES_H #include <inttypes.h> @@ -262,7 +236,7 @@ static inline void WPA_PUT_BE24(u8 *a, u32 val) static inline u32 WPA_GET_BE32(const u8 *a) { - return (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3]; + return ((u32) a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3]; } static inline void WPA_PUT_BE32(u8 *a, u32 val) @@ -275,7 +249,7 @@ static inline void WPA_PUT_BE32(u8 *a, u32 val) static inline u32 WPA_GET_LE32(const u8 *a) { - return (a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0]; + return ((u32) a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0]; } static inline void WPA_PUT_LE32(u8 *a, u32 val) @@ -433,7 +407,7 @@ void perror(const char *s); #endif #ifndef BIT -#define BIT(x) (1 << (x)) +#define BIT(x) (1U << (x)) #endif /* @@ -480,6 +454,8 @@ int hexstr2bin(const char *hex, u8 *buf, size_t len); void inc_byte_array(u8 *counter, size_t len); void wpa_get_ntp_timestamp(u8 *buf); int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...); +int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len, + char sep); int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len); int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data, size_t len); @@ -516,6 +492,11 @@ static inline int is_broadcast_ether_addr(const u8 *a) return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff; } +static inline int is_multicast_ether_addr(const u8 *a) +{ + return a[0] & 0x01; +} + #define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff" #include "wpa_debug.h" @@ -547,11 +528,13 @@ void bin_clear_free(void *bin, size_t len); int random_mac_addr(u8 *addr); int random_mac_addr_keep_oui(u8 *addr); +const char * cstr_token(const char *str, const char *delim, const char **last); char * str_token(char *str, const char *delim, char **context); size_t utf8_escape(const char *inp, size_t in_size, char *outp, size_t out_size); size_t utf8_unescape(const char *inp, size_t in_size, char *outp, size_t out_size); +int is_ctrl_char(char c); /* diff --git a/src/utils/eloop.c b/src/utils/eloop.c index 4a565ebdbd0a9..8647229b8eb5f 100644 --- a/src/utils/eloop.c +++ b/src/utils/eloop.c @@ -61,11 +61,8 @@ struct eloop_signal { struct eloop_sock_table { int count; struct eloop_sock *table; -#ifdef CONFIG_ELOOP_EPOLL eloop_event_type type; -#else /* CONFIG_ELOOP_EPOLL */ int changed; -#endif /* CONFIG_ELOOP_EPOLL */ }; struct eloop_data { @@ -256,9 +253,7 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, table->table = tmp; eloop.max_sock = new_max_sock; eloop.count++; -#ifndef CONFIG_ELOOP_EPOLL table->changed = 1; -#endif /* CONFIG_ELOOP_EPOLL */ eloop_trace_sock_add_ref(table); #ifdef CONFIG_ELOOP_EPOLL @@ -314,9 +309,7 @@ static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, } table->count--; eloop.count--; -#ifndef CONFIG_ELOOP_EPOLL table->changed = 1; -#endif /* CONFIG_ELOOP_EPOLL */ eloop_trace_sock_add_ref(table); #ifdef CONFIG_ELOOP_EPOLL if (epoll_ctl(eloop.epollfd, EPOLL_CTL_DEL, sock, NULL) < 0) { @@ -523,6 +516,10 @@ static void eloop_sock_table_dispatch(struct epoll_event *events, int nfds) continue; table->handler(table->sock, table->eloop_data, table->user_data); + if (eloop.readers.changed || + eloop.writers.changed || + eloop.exceptions.changed) + break; } } #endif /* CONFIG_ELOOP_EPOLL */ @@ -923,6 +920,20 @@ void eloop_run(void) (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 || eloop.writers.count > 0 || eloop.exceptions.count > 0)) { struct eloop_timeout *timeout; + + if (eloop.pending_terminate) { + /* + * This may happen in some corner cases where a signal + * is received during a blocking operation. We need to + * process the pending signals and exit if requested to + * avoid hitting the SIGALRM limit if the blocking + * operation took more than two seconds. + */ + eloop_process_pending_signals(); + if (eloop.terminate) + break; + } + timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, list); if (timeout) { @@ -977,6 +988,11 @@ void eloop_run(void) , strerror(errno)); goto out; } + + eloop.readers.changed = 0; + eloop.writers.changed = 0; + eloop.exceptions.changed = 0; + eloop_process_pending_signals(); /* check if some registered timeouts have occurred */ @@ -998,6 +1014,19 @@ void eloop_run(void) if (res <= 0) continue; + if (eloop.readers.changed || + eloop.writers.changed || + eloop.exceptions.changed) { + /* + * Sockets may have been closed and reopened with the + * same FD in the signal or timeout handlers, so we + * must skip the previous results and check again + * whether any of the currently registered sockets have + * events. + */ + continue; + } + #ifdef CONFIG_ELOOP_POLL eloop_sock_table_dispatch(&eloop.readers, &eloop.writers, &eloop.exceptions, eloop.pollfds_map, @@ -1073,7 +1102,7 @@ void eloop_destroy(void) int eloop_terminated(void) { - return eloop.terminate; + return eloop.terminate || eloop.pending_terminate; } diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c index b38cf796ca2ac..653eb541ab472 100644 --- a/src/utils/http_curl.c +++ b/src/utils/http_curl.c @@ -855,8 +855,10 @@ static int validate_server_cert(struct http_ctx *ctx, X509 *cert) struct http_cert hcert; int ret; - if (ctx->cert_cb == NULL) + if (ctx->cert_cb == NULL) { + wpa_printf(MSG_DEBUG, "%s: no cert_cb configured", __func__); return 0; + } if (0) { BIO *out; @@ -950,7 +952,8 @@ static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) ssl_ctx = ssl->ctx; ctx = SSL_CTX_get_app_data(ssl_ctx); - wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify"); + wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify, preverify_ok: %d", + preverify_ok); err = X509_STORE_CTX_get_error(x509_ctx); err_str = X509_verify_cert_error_string(err); @@ -1249,9 +1252,14 @@ static CURL * setup_curl_post(struct http_ctx *ctx, const char *address, const char *client_key) { CURL *curl; +#ifdef EAP_TLS_OPENSSL + const char *extra = " tls=openssl"; +#else /* EAP_TLS_OPENSSL */ + const char *extra = ""; +#endif /* EAP_TLS_OPENSSL */ wpa_printf(MSG_DEBUG, "Start HTTP client: address=%s ca_fname=%s " - "username=%s", address, ca_fname, username); + "username=%s%s", address, ca_fname, username, extra); curl = curl_easy_init(); if (curl == NULL) diff --git a/src/utils/includes.h b/src/utils/includes.h index 6c6ec87d0eaf4..75513fc8c1efd 100644 --- a/src/utils/includes.h +++ b/src/utils/includes.h @@ -17,26 +17,22 @@ #include "build_config.h" #include <stdlib.h> +#include <stddef.h> #include <stdio.h> #include <stdarg.h> #include <string.h> #ifndef _WIN32_WCE -#ifndef CONFIG_TI_COMPILER #include <signal.h> #include <sys/types.h> -#endif /* CONFIG_TI_COMPILER */ #include <errno.h> #endif /* _WIN32_WCE */ #include <ctype.h> -#ifndef CONFIG_TI_COMPILER #ifndef _MSC_VER #include <unistd.h> #endif /* _MSC_VER */ -#endif /* CONFIG_TI_COMPILER */ #ifndef CONFIG_NATIVE_WINDOWS -#ifndef CONFIG_TI_COMPILER #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> @@ -44,7 +40,6 @@ #include <sys/uio.h> #include <sys/time.h> #endif /* __vxworks */ -#endif /* CONFIG_TI_COMPILER */ #endif /* CONFIG_NATIVE_WINDOWS */ #endif /* INCLUDES_H */ diff --git a/src/utils/os.h b/src/utils/os.h index 77250d6371c91..9e496fb659783 100644 --- a/src/utils/os.h +++ b/src/utils/os.h @@ -247,6 +247,13 @@ char * os_readfile(const char *name, size_t *len); int os_file_exists(const char *fname); /** + * os_fdatasync - Sync a file's (for a given stream) state with storage device + * @stream: the stream to be flushed + * Returns: 0 if the operation succeeded or -1 on failure + */ +int os_fdatasync(FILE *stream); + +/** * os_zalloc - Allocate and zero memory * @size: Number of bytes to allocate * Returns: Pointer to allocated and zeroed memory or %NULL on failure @@ -646,4 +653,12 @@ int os_exec(const char *program, const char *arg, int wait_completion); #define strcpy OS_DO_NOT_USE_strcpy #endif /* OS_REJECT_C_LIB_FUNCTIONS */ + +#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) +#define TEST_FAIL() testing_test_fail() +int testing_test_fail(void); +#else +#define TEST_FAIL() 0 +#endif + #endif /* OS_H */ diff --git a/src/utils/os_internal.c b/src/utils/os_internal.c index 77733ad916cd0..ed6eb3c6b6770 100644 --- a/src/utils/os_internal.c +++ b/src/utils/os_internal.c @@ -243,6 +243,12 @@ char * os_readfile(const char *name, size_t *len) } +int os_fdatasync(FILE *stream) +{ + return 0; +} + + void * os_zalloc(size_t size) { void *n = os_malloc(size); diff --git a/src/utils/os_none.c b/src/utils/os_none.c index 83fe025167b6f..0c3214d32ce50 100644 --- a/src/utils/os_none.c +++ b/src/utils/os_none.c @@ -102,6 +102,12 @@ char * os_readfile(const char *name, size_t *len) } +int os_fdatasync(FILE *stream) +{ + return 0; +} + + void * os_zalloc(size_t size) { return NULL; diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c index e0c1125d53051..ffa2e788b3db8 100644 --- a/src/utils/os_unix.c +++ b/src/utils/os_unix.c @@ -17,6 +17,12 @@ #include <private/android_filesystem_config.h> #endif /* ANDROID */ +#ifdef __MACH__ +#include <CoreServices/CoreServices.h> +#include <mach/mach.h> +#include <mach/mach_time.h> +#endif /* __MACH__ */ + #include "os.h" #include "common.h" @@ -36,7 +42,7 @@ struct os_alloc_trace { struct dl_list list; size_t len; WPA_TRACE_INFO -}; +} __attribute__((aligned(16))); #endif /* WPA_TRACE */ @@ -63,6 +69,7 @@ int os_get_time(struct os_time *t) int os_get_reltime(struct os_reltime *t) { +#ifndef __MACH__ #if defined(CLOCK_BOOTTIME) static clockid_t clock_id = CLOCK_BOOTTIME; #elif defined(CLOCK_MONOTONIC) @@ -95,6 +102,23 @@ int os_get_reltime(struct os_reltime *t) return -1; } } +#else /* __MACH__ */ + uint64_t abstime, nano; + static mach_timebase_info_data_t info = { 0, 0 }; + + if (!info.denom) { + if (mach_timebase_info(&info) != KERN_SUCCESS) + return -1; + } + + abstime = mach_absolute_time(); + nano = (abstime * info.numer) / info.denom; + + t->sec = nano / NSEC_PER_SEC; + t->usec = (nano - (((uint64_t) t->sec) * NSEC_PER_SEC)) / NSEC_PER_USEC; + + return 0; +#endif /* __MACH__ */ } @@ -226,6 +250,9 @@ int os_get_random(unsigned char *buf, size_t len) FILE *f; size_t rc; + if (TEST_FAIL()) + return -1; + f = fopen("/dev/urandom", "rb"); if (f == NULL) { printf("Could not open /dev/urandom.\n"); @@ -415,6 +442,25 @@ int os_file_exists(const char *fname) } +int os_fdatasync(FILE *stream) +{ + if (!fflush(stream)) { +#ifndef __MACH__ + return fdatasync(fileno(stream)); +#else /* __MACH__ */ +#ifdef F_FULLFSYNC + /* OS X does not implement fdatasync(). */ + return fcntl(fileno(stream), F_FULLFSYNC); +#else /* F_FULLFSYNC */ +#error Neither fdatasync nor F_FULLSYNC are defined +#endif /* F_FULLFSYNC */ +#endif /* __MACH__ */ + } + + return -1; +} + + #ifndef WPA_TRACE void * os_zalloc(size_t size) { @@ -548,6 +594,78 @@ static int testing_fail_alloc(void) return 0; } + +char wpa_trace_test_fail_func[256] = { 0 }; +unsigned int wpa_trace_test_fail_after; + +int testing_test_fail(void) +{ + const char *func[WPA_TRACE_LEN]; + size_t i, res, len; + char *pos, *next; + int match; + + if (!wpa_trace_test_fail_after) + return 0; + + res = wpa_trace_calling_func(func, WPA_TRACE_LEN); + i = 0; + if (i < res && os_strcmp(func[i], __func__) == 0) + i++; + + pos = wpa_trace_test_fail_func; + + match = 0; + while (i < res) { + int allow_skip = 1; + int maybe = 0; + + if (*pos == '=') { + allow_skip = 0; + pos++; + } else if (*pos == '?') { + maybe = 1; + pos++; + } + next = os_strchr(pos, ';'); + if (next) + len = next - pos; + else + len = os_strlen(pos); + if (os_memcmp(pos, func[i], len) != 0) { + if (maybe && next) { + pos = next + 1; + continue; + } + if (allow_skip) { + i++; + continue; + } + return 0; + } + if (!next) { + match = 1; + break; + } + pos = next + 1; + i++; + } + if (!match) + return 0; + + wpa_trace_test_fail_after--; + if (wpa_trace_test_fail_after == 0) { + wpa_printf(MSG_INFO, "TESTING: fail at %s", + wpa_trace_test_fail_func); + for (i = 0; i < res; i++) + wpa_printf(MSG_INFO, "backtrace[%d] = %s", + (int) i, func[i]); + return 1; + } + + return 0; +} + #else static inline int testing_fail_alloc(void) diff --git a/src/utils/os_win32.c b/src/utils/os_win32.c index 296ea13f153b4..dea27b9f2ad84 100644 --- a/src/utils/os_win32.c +++ b/src/utils/os_win32.c @@ -216,6 +216,24 @@ char * os_readfile(const char *name, size_t *len) } +int os_fdatasync(FILE *stream) +{ + HANDLE h; + + if (stream == NULL) + return -1; + + h = (HANDLE) _get_osfhandle(_fileno(stream)); + if (h == INVALID_HANDLE_VALUE) + return -1; + + if (!FlushFileBuffers(h)) + return -1; + + return 0; +} + + void * os_zalloc(size_t size) { return calloc(1, size); diff --git a/src/utils/radiotap.c b/src/utils/radiotap.c index f8f815a86be92..c9a5023355922 100644 --- a/src/utils/radiotap.c +++ b/src/utils/radiotap.c @@ -123,13 +123,13 @@ int ieee80211_radiotap_iterator_init( /* find payload start allowing for extended bitmap(s) */ - if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) { + if (iterator->_bitmap_shifter & BIT(IEEE80211_RADIOTAP_EXT)) { if ((unsigned long)iterator->_arg - (unsigned long)iterator->_rtheader + sizeof(uint32_t) > (unsigned long)iterator->_max_length) return -EINVAL; while (get_unaligned_le32(iterator->_arg) & - (1 << IEEE80211_RADIOTAP_EXT)) { + BIT(IEEE80211_RADIOTAP_EXT)) { iterator->_arg += sizeof(uint32_t); /* diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c index 4b97dadd786c6..41511b9999a62 100644 --- a/src/utils/utils_module_tests.c +++ b/src/utils/utils_module_tests.c @@ -9,10 +9,13 @@ #include "utils/includes.h" #include "utils/common.h" +#include "common/ieee802_11_defs.h" #include "utils/bitfield.h" #include "utils/ext_password.h" #include "utils/trace.h" #include "utils/base64.h" +#include "utils/ip_addr.h" +#include "utils/eloop.h" struct printf_test_data { @@ -43,6 +46,7 @@ static int printf_encode_decode_tests(void) char buf[100]; u8 bin[100]; int errors = 0; + int array[10]; wpa_printf(MSG_INFO, "printf encode/decode tests"); @@ -91,9 +95,24 @@ static int printf_encode_decode_tests(void) if (printf_decode(bin, 3, "\\xa") != 1 || bin[0] != 10) errors++; + if (printf_decode(bin, 3, "\\xq") != 1 || bin[0] != 'q') + errors++; + if (printf_decode(bin, 3, "\\a") != 1 || bin[0] != 'a') errors++; + array[0] = 10; + array[1] = 10; + array[2] = 5; + array[3] = 10; + array[4] = 5; + array[5] = 0; + if (int_array_len(array) != 5) + errors++; + int_array_sort_unique(array); + if (int_array_len(array) != 2) + errors++; + if (errors) { wpa_printf(MSG_ERROR, "%d printf test(s) failed", errors); return -1; @@ -335,11 +354,14 @@ static int base64_tests(void) static int common_tests(void) { - char buf[3]; + char buf[3], longbuf[100]; u8 addr[ETH_ALEN] = { 1, 2, 3, 4, 5, 6 }; u8 bin[3]; int errors = 0; struct wpa_freq_range_list ranges; + size_t len; + const char *txt; + u8 ssid[255]; wpa_printf(MSG_INFO, "common tests"); @@ -395,6 +417,21 @@ static int common_tests(void) if (utf8_escape("a", 0, buf, sizeof(buf)) != 1 || buf[0] != 'a') errors++; + os_memset(ssid, 0, sizeof(ssid)); + txt = wpa_ssid_txt(ssid, sizeof(ssid)); + len = os_strlen(txt); + /* Verify that SSID_MAX_LEN * 4 buffer limit is enforced. */ + if (len != SSID_MAX_LEN * 4) { + wpa_printf(MSG_ERROR, + "Unexpected wpa_ssid_txt() result with too long SSID"); + errors++; + } + + if (wpa_snprintf_hex_sep(longbuf, 0, addr, ETH_ALEN, '-') != 0 || + wpa_snprintf_hex_sep(longbuf, 5, addr, ETH_ALEN, '-') != 3 || + os_strcmp(longbuf, "01-0") != 0) + errors++; + if (errors) { wpa_printf(MSG_ERROR, "%d common test(s) failed", errors); return -1; @@ -404,6 +441,403 @@ static int common_tests(void) } +static int os_tests(void) +{ + int errors = 0; + void *ptr; + os_time_t t; + + wpa_printf(MSG_INFO, "os tests"); + + ptr = os_calloc((size_t) -1, (size_t) -1); + if (ptr) { + errors++; + os_free(ptr); + } + ptr = os_calloc((size_t) 2, (size_t) -1); + if (ptr) { + errors++; + os_free(ptr); + } + ptr = os_calloc((size_t) -1, (size_t) 2); + if (ptr) { + errors++; + os_free(ptr); + } + + ptr = os_realloc_array(NULL, (size_t) -1, (size_t) -1); + if (ptr) { + errors++; + os_free(ptr); + } + + os_sleep(1, 1); + + if (os_mktime(1969, 1, 1, 1, 1, 1, &t) == 0 || + os_mktime(1971, 0, 1, 1, 1, 1, &t) == 0 || + os_mktime(1971, 13, 1, 1, 1, 1, &t) == 0 || + os_mktime(1971, 1, 0, 1, 1, 1, &t) == 0 || + os_mktime(1971, 1, 32, 1, 1, 1, &t) == 0 || + os_mktime(1971, 1, 1, -1, 1, 1, &t) == 0 || + os_mktime(1971, 1, 1, 24, 1, 1, &t) == 0 || + os_mktime(1971, 1, 1, 1, -1, 1, &t) == 0 || + os_mktime(1971, 1, 1, 1, 60, 1, &t) == 0 || + os_mktime(1971, 1, 1, 1, 1, -1, &t) == 0 || + os_mktime(1971, 1, 1, 1, 1, 61, &t) == 0 || + os_mktime(1971, 1, 1, 1, 1, 1, &t) != 0 || + os_mktime(2020, 1, 2, 3, 4, 5, &t) != 0 || + os_mktime(2015, 12, 31, 23, 59, 59, &t) != 0) + errors++; + + if (os_setenv("hwsim_test_env", "test value", 0) != 0 || + os_setenv("hwsim_test_env", "test value 2", 1) != 0 || + os_unsetenv("hwsim_test_env") != 0) + errors++; + + if (os_file_exists("/this-file-does-not-exists-hwsim") != 0) + errors++; + + if (errors) { + wpa_printf(MSG_ERROR, "%d os test(s) failed", errors); + return -1; + } + + return 0; +} + + +static int wpabuf_tests(void) +{ + int errors = 0; + void *ptr; + struct wpabuf *buf; + + wpa_printf(MSG_INFO, "wpabuf tests"); + + ptr = os_malloc(100); + if (ptr) { + buf = wpabuf_alloc_ext_data(ptr, 100); + if (buf) { + if (wpabuf_resize(&buf, 100) < 0) + errors++; + else + wpabuf_put(buf, 100); + wpabuf_free(buf); + } else { + errors++; + os_free(ptr); + } + } else { + errors++; + } + + buf = wpabuf_alloc(100); + if (buf) { + struct wpabuf *buf2; + + wpabuf_put(buf, 100); + if (wpabuf_resize(&buf, 100) < 0) + errors++; + else + wpabuf_put(buf, 100); + buf2 = wpabuf_concat(buf, NULL); + if (buf2 != buf) + errors++; + wpabuf_free(buf2); + } else { + errors++; + } + + buf = NULL; + buf = wpabuf_zeropad(buf, 10); + if (buf != NULL) + errors++; + + if (errors) { + wpa_printf(MSG_ERROR, "%d wpabuf test(s) failed", errors); + return -1; + } + + return 0; +} + + +static int ip_addr_tests(void) +{ + int errors = 0; + struct hostapd_ip_addr addr; + char buf[100]; + + wpa_printf(MSG_INFO, "ip_addr tests"); + + if (hostapd_parse_ip_addr("1.2.3.4", &addr) != 0 || + addr.af != AF_INET || + hostapd_ip_txt(NULL, buf, sizeof(buf)) != NULL || + hostapd_ip_txt(&addr, buf, 1) != buf || buf[0] != '\0' || + hostapd_ip_txt(&addr, buf, 0) != NULL || + hostapd_ip_txt(&addr, buf, sizeof(buf)) != buf) + errors++; + + if (hostapd_parse_ip_addr("::", &addr) != 0 || + addr.af != AF_INET6 || + hostapd_ip_txt(&addr, buf, 1) != buf || buf[0] != '\0' || + hostapd_ip_txt(&addr, buf, sizeof(buf)) != buf) + errors++; + + if (errors) { + wpa_printf(MSG_ERROR, "%d ip_addr test(s) failed", errors); + return -1; + } + + return 0; +} + + +struct test_eloop { + unsigned int magic; + int close_in_timeout; + int pipefd1[2]; + int pipefd2[2]; +}; + + +static void eloop_tests_start(int close_in_timeout); + + +static void eloop_test_read_2(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct test_eloop *t = eloop_ctx; + ssize_t res; + char buf[10]; + + wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock); + + if (t->magic != 0x12345678) { + wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x", + __func__, t->magic); + } + + if (t->pipefd2[0] != sock) { + wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d", + __func__, sock, t->pipefd2[0]); + } + + res = read(sock, buf, sizeof(buf)); + wpa_printf(MSG_INFO, "%s: sock=%d --> res=%d", + __func__, sock, (int) res); +} + + +static void eloop_test_read_2_wrong(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct test_eloop *t = eloop_ctx; + + wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock); + + if (t->magic != 0x12345678) { + wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x", + __func__, t->magic); + } + + if (t->pipefd2[0] != sock) { + wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d", + __func__, sock, t->pipefd2[0]); + } + + /* + * This is expected to block due to the original socket with data having + * been closed and no new data having been written to the new socket + * with the same fd. To avoid blocking the process during test, skip the + * read here. + */ + wpa_printf(MSG_ERROR, "%s: FAIL - should not have called this function", + __func__); +} + + +static void reopen_pipefd2(struct test_eloop *t) +{ + if (t->pipefd2[0] < 0) { + wpa_printf(MSG_INFO, "pipefd2 had been closed"); + } else { + int res; + + wpa_printf(MSG_INFO, "close pipefd2"); + eloop_unregister_read_sock(t->pipefd2[0]); + close(t->pipefd2[0]); + t->pipefd2[0] = -1; + close(t->pipefd2[1]); + t->pipefd2[1] = -1; + + res = pipe(t->pipefd2); + if (res < 0) { + wpa_printf(MSG_INFO, "pipe: %s", strerror(errno)); + t->pipefd2[0] = -1; + t->pipefd2[1] = -1; + return; + } + + wpa_printf(MSG_INFO, + "re-register pipefd2 with new sockets %d,%d", + t->pipefd2[0], t->pipefd2[1]); + eloop_register_read_sock(t->pipefd2[0], eloop_test_read_2_wrong, + t, NULL); + } +} + + +static void eloop_test_read_1(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct test_eloop *t = eloop_ctx; + ssize_t res; + char buf[10]; + + wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock); + + if (t->magic != 0x12345678) { + wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x", + __func__, t->magic); + } + + if (t->pipefd1[0] != sock) { + wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d", + __func__, sock, t->pipefd1[0]); + } + + res = read(sock, buf, sizeof(buf)); + wpa_printf(MSG_INFO, "%s: sock=%d --> res=%d", + __func__, sock, (int) res); + + if (!t->close_in_timeout) + reopen_pipefd2(t); +} + + +static void eloop_test_cb(void *eloop_data, void *user_ctx) +{ + struct test_eloop *t = eloop_data; + + wpa_printf(MSG_INFO, "%s", __func__); + + if (t->magic != 0x12345678) { + wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x", + __func__, t->magic); + } + + if (t->close_in_timeout) + reopen_pipefd2(t); +} + + +static void eloop_test_timeout(void *eloop_data, void *user_ctx) +{ + struct test_eloop *t = eloop_data; + int next_run = 0; + + wpa_printf(MSG_INFO, "%s", __func__); + + if (t->magic != 0x12345678) { + wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x", + __func__, t->magic); + } + + if (t->pipefd1[0] >= 0) { + wpa_printf(MSG_INFO, "pipefd1 had not been closed"); + eloop_unregister_read_sock(t->pipefd1[0]); + close(t->pipefd1[0]); + t->pipefd1[0] = -1; + close(t->pipefd1[1]); + t->pipefd1[1] = -1; + } + + if (t->pipefd2[0] >= 0) { + wpa_printf(MSG_INFO, "pipefd2 had not been closed"); + eloop_unregister_read_sock(t->pipefd2[0]); + close(t->pipefd2[0]); + t->pipefd2[0] = -1; + close(t->pipefd2[1]); + t->pipefd2[1] = -1; + } + + next_run = t->close_in_timeout; + t->magic = 0; + wpa_printf(MSG_INFO, "%s - free(%p)", __func__, t); + os_free(t); + + if (next_run) + eloop_tests_start(0); +} + + +static void eloop_tests_start(int close_in_timeout) +{ + struct test_eloop *t; + int res; + + t = os_zalloc(sizeof(*t)); + if (!t) + return; + t->magic = 0x12345678; + t->close_in_timeout = close_in_timeout; + + wpa_printf(MSG_INFO, "starting eloop tests (%p) (close_in_timeout=%d)", + t, close_in_timeout); + + res = pipe(t->pipefd1); + if (res < 0) { + wpa_printf(MSG_INFO, "pipe: %s", strerror(errno)); + os_free(t); + return; + } + + res = pipe(t->pipefd2); + if (res < 0) { + wpa_printf(MSG_INFO, "pipe: %s", strerror(errno)); + close(t->pipefd1[0]); + close(t->pipefd1[1]); + os_free(t); + return; + } + + wpa_printf(MSG_INFO, "pipe fds: %d,%d %d,%d", + t->pipefd1[0], t->pipefd1[1], + t->pipefd2[0], t->pipefd2[1]); + + eloop_register_read_sock(t->pipefd1[0], eloop_test_read_1, t, NULL); + eloop_register_read_sock(t->pipefd2[0], eloop_test_read_2, t, NULL); + eloop_register_timeout(0, 0, eloop_test_cb, t, NULL); + eloop_register_timeout(0, 200000, eloop_test_timeout, t, NULL); + + if (write(t->pipefd1[1], "HELLO", 5) < 0) + wpa_printf(MSG_INFO, "write: %s", strerror(errno)); + if (write(t->pipefd2[1], "TEST", 4) < 0) + wpa_printf(MSG_INFO, "write: %s", strerror(errno)); + os_sleep(0, 50000); + wpa_printf(MSG_INFO, "waiting for eloop callbacks"); +} + + +static void eloop_tests_run(void *eloop_data, void *user_ctx) +{ + eloop_tests_start(1); +} + + +static int eloop_tests(void) +{ + wpa_printf(MSG_INFO, "schedule eloop tests to be run"); + + /* + * Cannot return error from these without a significant design change, + * so for now, run the tests from a scheduled timeout and require + * separate verification of the results from the debug log. + */ + eloop_register_timeout(0, 0, eloop_tests_run, NULL, NULL); + + return 0; +} + + int utils_module_tests(void) { int ret = 0; @@ -416,6 +850,10 @@ int utils_module_tests(void) bitfield_tests() < 0 || base64_tests() < 0 || common_tests() < 0 || + os_tests() < 0 || + wpabuf_tests() < 0 || + ip_addr_tests() < 0 || + eloop_tests() < 0 || int_array_tests() < 0) ret = -1; diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c index 0d11905185365..61c0d5ce68c79 100644 --- a/src/utils/wpa_debug.c +++ b/src/utils/wpa_debug.c @@ -307,7 +307,7 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf, "%s - hexdump(len=%lu):%s%s", title, (long unsigned int) len, display, len > slen ? " ..." : ""); - os_free(strbuf); + bin_clear_free(strbuf, 1 + 3 * slen); return; } #else /* CONFIG_ANDROID_LOG */ @@ -339,7 +339,7 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf, syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s", title, (unsigned long) len, display); - os_free(strbuf); + bin_clear_free(strbuf, 1 + 3 * len); return; } #endif /* CONFIG_DEBUG_SYSLOG */ @@ -635,8 +635,8 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...) va_end(ap); wpa_printf(level, "%s%s", prefix, buf); if (wpa_msg_cb) - wpa_msg_cb(ctx, level, 0, buf, len); - os_free(buf); + wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len); + bin_clear_free(buf, buflen); } @@ -663,8 +663,8 @@ void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...) va_start(ap, fmt); len = vsnprintf(buf, buflen, fmt, ap); va_end(ap); - wpa_msg_cb(ctx, level, 0, buf, len); - os_free(buf); + wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len); + bin_clear_free(buf, buflen); } @@ -690,8 +690,8 @@ void wpa_msg_global(void *ctx, int level, const char *fmt, ...) va_end(ap); wpa_printf(level, "%s", buf); if (wpa_msg_cb) - wpa_msg_cb(ctx, level, 1, buf, len); - os_free(buf); + wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len); + bin_clear_free(buf, buflen); } @@ -718,8 +718,8 @@ void wpa_msg_global_ctrl(void *ctx, int level, const char *fmt, ...) va_start(ap, fmt); len = vsnprintf(buf, buflen, fmt, ap); va_end(ap); - wpa_msg_cb(ctx, level, 1, buf, len); - os_free(buf); + wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len); + bin_clear_free(buf, buflen); } @@ -745,7 +745,34 @@ void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...) va_end(ap); wpa_printf(level, "%s", buf); if (wpa_msg_cb) - wpa_msg_cb(ctx, level, 2, buf, len); + wpa_msg_cb(ctx, level, WPA_MSG_NO_GLOBAL, buf, len); + bin_clear_free(buf, buflen); +} + + +void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...) +{ + va_list ap; + char *buf; + int buflen; + int len; + + va_start(ap, fmt); + buflen = vsnprintf(NULL, 0, fmt, ap) + 1; + va_end(ap); + + buf = os_malloc(buflen); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate message buffer", + __func__); + return; + } + va_start(ap, fmt); + len = vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + wpa_printf(level, "%s", buf); + if (wpa_msg_cb) + wpa_msg_cb(ctx, level, WPA_MSG_ONLY_GLOBAL, buf, len); os_free(buf); } @@ -789,6 +816,45 @@ void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level, MAC2STR(addr), buf); else wpa_printf(MSG_DEBUG, "hostapd_logger: %s", buf); - os_free(buf); + bin_clear_free(buf, buflen); } #endif /* CONFIG_NO_HOSTAPD_LOGGER */ + + +const char * debug_level_str(int level) +{ + switch (level) { + case MSG_EXCESSIVE: + return "EXCESSIVE"; + case MSG_MSGDUMP: + return "MSGDUMP"; + case MSG_DEBUG: + return "DEBUG"; + case MSG_INFO: + return "INFO"; + case MSG_WARNING: + return "WARNING"; + case MSG_ERROR: + return "ERROR"; + default: + return "?"; + } +} + + +int str_to_debug_level(const char *s) +{ + if (os_strcasecmp(s, "EXCESSIVE") == 0) + return MSG_EXCESSIVE; + if (os_strcasecmp(s, "MSGDUMP") == 0) + return MSG_MSGDUMP; + if (os_strcasecmp(s, "DEBUG") == 0) + return MSG_DEBUG; + if (os_strcasecmp(s, "INFO") == 0) + return MSG_INFO; + if (os_strcasecmp(s, "WARNING") == 0) + return MSG_WARNING; + if (os_strcasecmp(s, "ERROR") == 0) + return MSG_ERROR; + return -1; +} diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h index 400bea9e599fd..17d8f963802e4 100644 --- a/src/utils/wpa_debug.h +++ b/src/utils/wpa_debug.h @@ -164,6 +164,7 @@ void wpa_hexdump_ascii_key(int level, const char *title, const void *buf, #define wpa_msg_global(args...) do { } while (0) #define wpa_msg_global_ctrl(args...) do { } while (0) #define wpa_msg_no_global(args...) do { } while (0) +#define wpa_msg_global_only(args...) do { } while (0) #define wpa_msg_register_cb(f) do { } while (0) #define wpa_msg_register_ifname_cb(f) do { } while (0) #else /* CONFIG_NO_WPA_MSG */ @@ -243,7 +244,28 @@ PRINTF_FORMAT(3, 4); void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4); -typedef void (*wpa_msg_cb_func)(void *ctx, int level, int global, +/** + * wpa_msg_global_only - Conditional printf for ctrl_iface monitors + * @ctx: Pointer to context data; this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. + * This function is like wpa_msg_global(), but it sends the output only as a + * global event. + */ +void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...) +PRINTF_FORMAT(3, 4); + +enum wpa_msg_type { + WPA_MSG_PER_INTERFACE, + WPA_MSG_GLOBAL, + WPA_MSG_NO_GLOBAL, + WPA_MSG_ONLY_GLOBAL, +}; + +typedef void (*wpa_msg_cb_func)(void *ctx, int level, enum wpa_msg_type type, const char *txt, size_t len); /** @@ -342,4 +364,7 @@ static inline void wpa_debug_close_linux_tracing(void) #define WPA_ASSERT(a) do { } while (0) #endif +const char * debug_level_str(int level); +int str_to_debug_level(const char *s); + #endif /* WPA_DEBUG_H */ diff --git a/src/utils/wpabuf.c b/src/utils/wpabuf.c index 7aafa0a5169ba..11e7323619de8 100644 --- a/src/utils/wpabuf.c +++ b/src/utils/wpabuf.c @@ -17,7 +17,7 @@ struct wpabuf_trace { unsigned int magic; -}; +} __attribute__((aligned(8))); static struct wpabuf_trace * wpabuf_get_trace(const struct wpabuf *buf) { diff --git a/src/wps/Makefile b/src/wps/Makefile index adfd3dfd5b9be..4806fe8dadf7e 100644 --- a/src/wps/Makefile +++ b/src/wps/Makefile @@ -1,8 +1,41 @@ -all: - @echo Nothing to be made. +all: libwps.a clean: - rm -f *~ *.o *.d *.gcno *.gcda *.gcov + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libwps.a install: @echo Nothing to be made. + +include ../lib.rules + +CFLAGS += -DCONFIG_P2P +CFLAGS += -DCONFIG_WPS_OOB +CFLAGS += -DCONFIG_WPS_NFC + +LIB_OBJS= \ + http_client.o \ + httpread.o \ + http_server.o \ + ndef.o \ + upnp_xml.o \ + wps_attr_build.o \ + wps_attr_parse.o \ + wps_attr_process.o \ + wps.o \ + wps_common.o \ + wps_dev_attr.o \ + wps_enrollee.o \ + wps_er.o \ + wps_er_ssdp.o \ + wps_module_tests.o \ + wps_registrar.o \ + wps_upnp_ap.o \ + wps_upnp.o \ + wps_upnp_event.o \ + wps_upnp_ssdp.o \ + wps_upnp_web.o + +libwps.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/src/wps/http_client.c b/src/wps/http_client.c index 029001306cbe4..cdf3a5128ed39 100644 --- a/src/wps/http_client.c +++ b/src/wps/http_client.c @@ -85,15 +85,16 @@ static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) { struct http_client *c = eloop_ctx; int res; + size_t send_len; + send_len = wpabuf_len(c->req) - c->req_pos; wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu " "bytes remaining)", inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port), (unsigned long) wpabuf_len(c->req), - (unsigned long) wpabuf_len(c->req) - c->req_pos); + (unsigned long) send_len); - res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos, - wpabuf_len(c->req) - c->req_pos, 0); + res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos, send_len, 0); if (res < 0) { wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s", strerror(errno)); @@ -102,12 +103,11 @@ static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) return; } - if ((size_t) res < wpabuf_len(c->req) - c->req_pos) { + if ((size_t) res < send_len) { wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes " "remaining", res, (unsigned long) wpabuf_len(c->req), - (unsigned long) wpabuf_len(c->req) - c->req_pos - - res); + (unsigned long) send_len - res); c->req_pos += res; return; } @@ -146,24 +146,20 @@ struct http_client * http_client_addr(struct sockaddr_in *dst, c->cb_ctx = cb_ctx; c->sd = socket(AF_INET, SOCK_STREAM, 0); - if (c->sd < 0) { - http_client_free(c); - return NULL; - } + if (c->sd < 0) + goto fail; if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) { wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s", strerror(errno)); - http_client_free(c); - return NULL; + goto fail; } if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) { if (errno != EINPROGRESS) { wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s", strerror(errno)); - http_client_free(c); - return NULL; + goto fail; } /* @@ -173,20 +169,18 @@ struct http_client * http_client_addr(struct sockaddr_in *dst, } if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready, - c, NULL)) { - http_client_free(c); - return NULL; - } - - if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0, - http_client_timeout, c, NULL)) { - http_client_free(c); - return NULL; - } + c, NULL) || + eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0, + http_client_timeout, c, NULL)) + goto fail; c->req = req; return c; + +fail: + http_client_free(c); + return NULL; } diff --git a/src/wps/http_server.c b/src/wps/http_server.c index ac088c429d606..507abe8707808 100644 --- a/src/wps/http_server.c +++ b/src/wps/http_server.c @@ -277,11 +277,9 @@ struct http_server * http_server_init(struct in_addr *addr, int port, "%s", srv->port, strerror(errno)); goto fail; } - if (listen(srv->fd, 10 /* max backlog */) < 0) - goto fail; - if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0) - goto fail; - if (eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb, + if (listen(srv->fd, 10 /* max backlog */) < 0 || + fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0 || + eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb, srv, NULL)) goto fail; diff --git a/src/wps/httpread.c b/src/wps/httpread.c index 2f08f37275c01..7a2ba50a9f314 100644 --- a/src/wps/httpread.c +++ b/src/wps/httpread.c @@ -44,16 +44,6 @@ #define HTTPREAD_HEADER_MAX_SIZE 4096 /* max allowed for headers */ #define HTTPREAD_BODYBUF_DELTA 4096 /* increase allocation by this */ -#if 0 -/* httpread_debug -- set this global variable > 0 e.g. from debugger - * to enable debugs (larger numbers for more debugs) - * Make this a #define of 0 to eliminate the debugging code. - */ -int httpread_debug = 99; -#else -#define httpread_debug 0 /* eliminates even the debugging code */ -#endif - /* control instance -- actual definition (opaque to application) */ @@ -136,8 +126,7 @@ static void httpread_timeout_handler(void *eloop_data, void *user_ctx); */ void httpread_destroy(struct httpread *h) { - if (httpread_debug >= 10) - wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h); + wpa_printf(MSG_DEBUG, "httpread_destroy(%p)", h); if (!h) return; @@ -177,6 +166,12 @@ static int httpread_hdr_option_analyze( if (!isdigit(*hbp)) return -1; h->content_length = atol(hbp); + if (h->content_length < 0 || h->content_length > h->max_bytes) { + wpa_printf(MSG_DEBUG, + "httpread: Unacceptable Content-Length %d", + h->content_length); + return -1; + } h->got_content_length = 1; return 0; } @@ -283,8 +278,6 @@ static int httpread_hdr_analyze(struct httpread *h) } } *uri = 0; /* null terminate */ - while (isgraph(*hbp)) - hbp++; while (*hbp == ' ' || *hbp == '\t') hbp++; /* get version */ @@ -380,15 +373,16 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) char *bbp; /* pointer into body buffer */ char readbuf[HTTPREAD_READBUF_SIZE]; /* temp use to read into */ - if (httpread_debug >= 20) - wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h); - /* read some at a time, then search for the interal * boundaries between header and data and etc. */ + wpa_printf(MSG_DEBUG, "httpread: Trying to read more data(%p)", h); nread = read(h->sd, readbuf, sizeof(readbuf)); - if (nread < 0) + if (nread < 0) { + wpa_printf(MSG_DEBUG, "httpread failed: %s", strerror(errno)); goto bad; + } + wpa_hexdump_ascii(MSG_MSGDUMP, "httpread - read", readbuf, nread); if (nread == 0) { /* end of transmission... this may be normal * or may be an error... in some cases we can't @@ -411,8 +405,7 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) * although dropped connections can cause false * end */ - if (httpread_debug >= 10) - wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h); + wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h); h->got_body = 1; goto got_file; } @@ -432,6 +425,8 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) if (nread == 0) goto get_more; if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) { + wpa_printf(MSG_DEBUG, + "httpread: Too long header"); goto bad; } *hbp++ = *rbp++; @@ -453,16 +448,13 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) goto bad; } if (h->max_bytes == 0) { - if (httpread_debug >= 10) - wpa_printf(MSG_DEBUG, - "httpread no body hdr end(%p)", h); + wpa_printf(MSG_DEBUG, "httpread no body hdr end(%p)", + h); goto got_file; } if (h->got_content_length && h->content_length == 0) { - if (httpread_debug >= 10) - wpa_printf(MSG_DEBUG, - "httpread zero content length(%p)", - h); + wpa_printf(MSG_DEBUG, + "httpread zero content length(%p)", h); goto got_file; } } @@ -475,9 +467,7 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) !os_strncasecmp(h->hdr, "HEAD", 4) || !os_strncasecmp(h->hdr, "GET", 3)) { if (!h->got_body) { - if (httpread_debug >= 10) - wpa_printf(MSG_DEBUG, - "httpread NO BODY for sp. type"); + wpa_printf(MSG_DEBUG, "httpread NO BODY for sp. type"); } h->got_body = 1; goto got_file; @@ -498,8 +488,12 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) char *new_body; int new_alloc_nbytes; - if (h->body_nbytes >= h->max_bytes) + if (h->body_nbytes >= h->max_bytes) { + wpa_printf(MSG_DEBUG, + "httpread: body_nbytes=%d >= max_bytes=%d", + h->body_nbytes, h->max_bytes); goto bad; + } new_alloc_nbytes = h->body_alloc_nbytes + HTTPREAD_BODYBUF_DELTA; /* For content-length case, the first time @@ -509,9 +503,23 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) if (h->got_content_length && new_alloc_nbytes < (h->content_length + 1)) new_alloc_nbytes = h->content_length + 1; + if (new_alloc_nbytes < h->body_alloc_nbytes || + new_alloc_nbytes > h->max_bytes + + HTTPREAD_BODYBUF_DELTA) { + wpa_printf(MSG_DEBUG, + "httpread: Unacceptable body length %d (body_alloc_nbytes=%u max_bytes=%u)", + new_alloc_nbytes, + h->body_alloc_nbytes, + h->max_bytes); + goto bad; + } if ((new_body = os_realloc(h->body, new_alloc_nbytes)) - == NULL) + == NULL) { + wpa_printf(MSG_DEBUG, + "httpread: Failed to reallocate buffer (len=%d)", + new_alloc_nbytes); goto bad; + } h->body = new_body; h->body_alloc_nbytes = new_alloc_nbytes; @@ -530,9 +538,19 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) /* hdr line consists solely * of a hex numeral and CFLF */ - if (!isxdigit(*cbp)) + if (!isxdigit(*cbp)) { + wpa_printf(MSG_DEBUG, + "httpread: Unexpected chunk header value (not a hex digit)"); goto bad; + } h->chunk_size = strtoul(cbp, NULL, 16); + if (h->chunk_size < 0 || + h->chunk_size > h->max_bytes) { + wpa_printf(MSG_DEBUG, + "httpread: Invalid chunk size %d", + h->chunk_size); + goto bad; + } /* throw away chunk header * so we have only real data */ @@ -542,10 +560,9 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) /* end of chunking */ /* trailer follows */ h->in_trailer = 1; - if (httpread_debug >= 20) - wpa_printf( - MSG_DEBUG, - "httpread end chunks(%p)", h); + wpa_printf(MSG_DEBUG, + "httpread end chunks(%p)", + h); break; } h->in_chunk_data = 1; @@ -563,8 +580,11 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) */ if (bbp[-1] == '\n' && bbp[-2] == '\r') { - } else + } else { + wpa_printf(MSG_DEBUG, + "httpread: Invalid chunk end"); goto bad; + } h->body_nbytes -= 2; bbp -= 2; h->chunk_start = h->body_nbytes; @@ -574,10 +594,8 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) } else if (h->got_content_length && h->body_nbytes >= h->content_length) { h->got_body = 1; - if (httpread_debug >= 10) - wpa_printf( - MSG_DEBUG, - "httpread got content(%p)", h); + wpa_printf(MSG_DEBUG, + "httpread got content(%p)", h); goto got_file; } if (nread <= 0) @@ -601,6 +619,11 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) ncopy = nread; } /* Note: should never be 0 */ + if (ncopy < 0) { + wpa_printf(MSG_DEBUG, + "httpread: Invalid ncopy=%d", ncopy); + goto bad; + } if (ncopy > nread) ncopy = nread; os_memcpy(bbp, rbp, ncopy); @@ -635,10 +658,9 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) if (c == '\n') { h->trailer_state = trailer_line_begin; h->in_trailer = 0; - if (httpread_debug >= 10) - wpa_printf( - MSG_DEBUG, - "httpread got content(%p)", h); + wpa_printf(MSG_DEBUG, + "httpread got content(%p)", + h); h->got_body = 1; goto got_file; } @@ -666,13 +688,14 @@ bad: return; get_more: + wpa_printf(MSG_DEBUG, "httpread: get more (%p)", h); return; got_file: - if (httpread_debug >= 10) - wpa_printf(MSG_DEBUG, - "httpread got file %d bytes type %d", - h->body_nbytes, h->hdr_type); + wpa_printf(MSG_DEBUG, "httpread got file %d bytes type %d", + h->body_nbytes, h->hdr_type); + wpa_hexdump_ascii(MSG_MSGDUMP, "httpread: body", + h->body, h->body_nbytes); /* Null terminate for convenience of some applications */ if (h->body) h->body[h->body_nbytes] = 0; /* null terminate */ diff --git a/src/wps/ndef.c b/src/wps/ndef.c index d45dfc8efee64..bb3c055486c02 100644 --- a/src/wps/ndef.c +++ b/src/wps/ndef.c @@ -29,8 +29,8 @@ struct ndef_record { u32 total_length; }; -static char wifi_handover_type[] = "application/vnd.wfa.wsc"; -static char p2p_handover_type[] = "application/vnd.wfa.p2p"; +static const char wifi_handover_type[] = "application/vnd.wfa.wsc"; +static const char p2p_handover_type[] = "application/vnd.wfa.p2p"; static int ndef_parse_record(const u8 *data, u32 size, struct ndef_record *record) @@ -45,9 +45,14 @@ static int ndef_parse_record(const u8 *data, u32 size, return -1; record->payload_length = *pos++; } else { + u32 len; + if (size < 6) return -1; - record->payload_length = ntohl(*(u32 *)pos); + len = WPA_GET_BE32(pos); + if (len > size - 6 || len > 20000) + return -1; + record->payload_length = len; pos += sizeof(u32); } @@ -68,7 +73,8 @@ static int ndef_parse_record(const u8 *data, u32 size, pos += record->payload_length; record->total_length = pos - data; - if (record->total_length > size) + if (record->total_length > size || + record->total_length < record->payload_length) return -1; return 0; } @@ -97,7 +103,7 @@ static struct wpabuf * ndef_parse_records(const struct wpabuf *buf, } -static struct wpabuf * ndef_build_record(u8 flags, void *type, +static struct wpabuf * ndef_build_record(u8 flags, const void *type, u8 type_length, void *id, u8 id_length, const struct wpabuf *payload) diff --git a/src/wps/wps.c b/src/wps/wps.c index 2c68be8c62ea0..fbaf85aabab4a 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -355,16 +355,16 @@ int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr, int wps_ap_priority_compar(const struct wpabuf *wps_a, const struct wpabuf *wps_b) { - struct wps_parse_attr attr_a, attr_b; + struct wps_parse_attr attr; int sel_a, sel_b; - if (wps_a == NULL || wps_parse_msg(wps_a, &attr_a) < 0) + if (wps_a == NULL || wps_parse_msg(wps_a, &attr) < 0) return 1; - if (wps_b == NULL || wps_parse_msg(wps_b, &attr_b) < 0) - return -1; + sel_a = attr.selected_registrar && *attr.selected_registrar != 0; - sel_a = attr_a.selected_registrar && *attr_a.selected_registrar != 0; - sel_b = attr_b.selected_registrar && *attr_b.selected_registrar != 0; + if (wps_b == NULL || wps_parse_msg(wps_b, &attr) < 0) + return -1; + sel_b = attr.selected_registrar && *attr.selected_registrar != 0; if (sel_a && !sel_b) return -1; @@ -618,7 +618,8 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) if (str == NULL) return pos - buf; for (i = 0; i < attr.dev_name_len; i++) { - if (attr.dev_name[i] < 32) + if (attr.dev_name[i] == 0 || + is_ctrl_char(attr.dev_name[i])) str[i] = '_'; else str[i] = attr.dev_name[i]; diff --git a/src/wps/wps.h b/src/wps/wps.h index 0a7f65dfd6cb5..2c91d1678c157 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -9,6 +9,7 @@ #ifndef WPS_H #define WPS_H +#include "common/ieee802_11_defs.h" #include "wps_defs.h" /** @@ -44,7 +45,7 @@ struct wps_parse_attr; * @cred_attr_len: Length of cred_attr in octets */ struct wps_credential { - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u16 auth_type; u16 encr_type; @@ -78,7 +79,7 @@ struct wps_credential { * @sec_dev_type: Array of secondary device types * @num_sec_dev_type: Number of secondary device types * @os_version: OS Version - * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ flags) + * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ, WPS_RF_60GHZ flags) * @p2p: Whether the device is a P2P device */ struct wps_device_data { @@ -623,7 +624,7 @@ struct wps_context { * Credentials. In addition, AP uses it when acting as an Enrollee to * notify Registrar of the current configuration. */ - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; /** * ssid_len - Length of ssid in octets diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c index 40bc1ad2d2c5b..11a967ba0ef18 100644 --- a/src/wps/wps_attr_parse.c +++ b/src/wps/wps_attr_parse.c @@ -447,25 +447,55 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type, break; case ATTR_MANUFACTURER: attr->manufacturer = pos; - attr->manufacturer_len = len; + if (len > WPS_MANUFACTURER_MAX_LEN) + attr->manufacturer_len = WPS_MANUFACTURER_MAX_LEN; + else + attr->manufacturer_len = len; break; case ATTR_MODEL_NAME: attr->model_name = pos; - attr->model_name_len = len; + if (len > WPS_MODEL_NAME_MAX_LEN) + attr->model_name_len = WPS_MODEL_NAME_MAX_LEN; + else + attr->model_name_len = len; break; case ATTR_MODEL_NUMBER: attr->model_number = pos; - attr->model_number_len = len; + if (len > WPS_MODEL_NUMBER_MAX_LEN) + attr->model_number_len = WPS_MODEL_NUMBER_MAX_LEN; + else + attr->model_number_len = len; break; case ATTR_SERIAL_NUMBER: attr->serial_number = pos; - attr->serial_number_len = len; + if (len > WPS_SERIAL_NUMBER_MAX_LEN) + attr->serial_number_len = WPS_SERIAL_NUMBER_MAX_LEN; + else + attr->serial_number_len = len; break; case ATTR_DEV_NAME: + if (len > WPS_DEV_NAME_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "WPS: Ignore too long Device Name (len=%u)", + len); + break; + } attr->dev_name = pos; attr->dev_name_len = len; break; case ATTR_PUBLIC_KEY: + /* + * The Public Key attribute is supposed to be exactly 192 bytes + * in length. Allow couple of bytes shorter one to try to + * interoperate with implementations that do not use proper + * zero-padding. + */ + if (len < 190 || len > 192) { + wpa_printf(MSG_DEBUG, + "WPS: Ignore Public Key with unexpected length %u", + len); + break; + } attr->public_key = pos; attr->public_key_len = len; break; @@ -485,6 +515,11 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type, attr->num_cred++; break; case ATTR_SSID: + if (len > SSID_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "WPS: Ignore too long SSID (len=%u)", len); + break; + } attr->ssid = pos; attr->ssid_len = len; break; diff --git a/src/wps/wps_attr_parse.h b/src/wps/wps_attr_parse.h index 82c4739f61f53..8188fe9173d44 100644 --- a/src/wps/wps_attr_parse.h +++ b/src/wps/wps_attr_parse.h @@ -59,43 +59,44 @@ struct wps_parse_attr { /* variable length fields */ const u8 *manufacturer; - size_t manufacturer_len; const u8 *model_name; - size_t model_name_len; const u8 *model_number; - size_t model_number_len; const u8 *serial_number; - size_t serial_number_len; const u8 *dev_name; - size_t dev_name_len; const u8 *public_key; - size_t public_key_len; const u8 *encr_settings; - size_t encr_settings_len; const u8 *ssid; /* <= 32 octets */ - size_t ssid_len; const u8 *network_key; /* <= 64 octets */ - size_t network_key_len; const u8 *authorized_macs; /* <= 30 octets */ - size_t authorized_macs_len; const u8 *sec_dev_type_list; /* <= 128 octets */ - size_t sec_dev_type_list_len; const u8 *oob_dev_password; /* 38..54 octets */ - size_t oob_dev_password_len; + u16 manufacturer_len; + u16 model_name_len; + u16 model_number_len; + u16 serial_number_len; + u16 dev_name_len; + u16 public_key_len; + u16 encr_settings_len; + u16 ssid_len; + u16 network_key_len; + u16 authorized_macs_len; + u16 sec_dev_type_list_len; + u16 oob_dev_password_len; /* attributes that can occur multiple times */ #define MAX_CRED_COUNT 10 - const u8 *cred[MAX_CRED_COUNT]; - size_t cred_len[MAX_CRED_COUNT]; - size_t num_cred; - #define MAX_REQ_DEV_TYPE_COUNT 10 - const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT]; - size_t num_req_dev_type; + unsigned int num_cred; + unsigned int num_req_dev_type; + unsigned int num_vendor_ext; + + u16 cred_len[MAX_CRED_COUNT]; + u16 vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT]; + + const u8 *cred[MAX_CRED_COUNT]; + const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT]; const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT]; - size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT]; - size_t num_vendor_ext; }; int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c index c1ede6a9ea834..88f85fe83f057 100644 --- a/src/wps/wps_common.c +++ b/src/wps/wps_common.c @@ -528,7 +528,7 @@ u16 wps_config_methods_str2bin(const char *str) { u16 methods = 0; - if (str == NULL) { + if (str == NULL || str[0] == '\0') { /* Default to enabling methods based on build configuration */ methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; methods |= WPS_CONFIG_VIRT_DISPLAY; @@ -764,6 +764,8 @@ static int wps_build_ap_freq(struct wpabuf *msg, int freq) rf_band = WPS_RF_24GHZ; else if (mode == HOSTAPD_MODE_IEEE80211A) rf_band = WPS_RF_50GHZ; + else if (mode == HOSTAPD_MODE_IEEE80211AD) + rf_band = WPS_RF_60GHZ; else return 0; /* Unknown band */ ap_channel = channel; diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h index 25cd14a0b32a0..a23b979d2e3c7 100644 --- a/src/wps/wps_defs.h +++ b/src/wps/wps_defs.h @@ -41,6 +41,11 @@ extern int wps_corrupt_pkhash; #define WPS_OOB_DEVICE_PASSWORD_MIN_LEN 16 #define WPS_OOB_DEVICE_PASSWORD_LEN 32 #define WPS_OOB_PUBKEY_HASH_LEN 20 +#define WPS_DEV_NAME_MAX_LEN 32 +#define WPS_MANUFACTURER_MAX_LEN 64 +#define WPS_MODEL_NAME_MAX_LEN 32 +#define WPS_MODEL_NUMBER_MAX_LEN 32 +#define WPS_SERIAL_NUMBER_MAX_LEN 32 /* Attribute Types */ enum wps_attribute { @@ -232,6 +237,7 @@ enum wps_error_indication { /* RF Bands */ #define WPS_RF_24GHZ 0x01 #define WPS_RF_50GHZ 0x02 +#define WPS_RF_60GHZ 0x04 /* Config Methods */ #define WPS_CONFIG_USBA 0x0001 diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c index 89957b1a818ad..9321b721abd75 100644 --- a/src/wps/wps_enrollee.c +++ b/src/wps/wps_enrollee.c @@ -759,7 +759,7 @@ static int wps_process_cred_e(struct wps_data *wps, const u8 *cred, static int wps_process_creds(struct wps_data *wps, const u8 *cred[], - size_t cred_len[], size_t num_cred, int wps2) + u16 cred_len[], unsigned int num_cred, int wps2) { size_t i; int ok = 0; @@ -799,6 +799,7 @@ static int wps_process_ap_settings_e(struct wps_data *wps, struct wpabuf *attrs, int wps2) { struct wps_credential cred; + int ret = 0; if (!wps->wps->ap) return 0; @@ -877,10 +878,10 @@ static int wps_process_ap_settings_e(struct wps_data *wps, if (wps->wps->cred_cb) { cred.cred_attr = wpabuf_head(attrs); cred.cred_attr_len = wpabuf_len(attrs); - wps->wps->cred_cb(wps->wps->cb_ctx, &cred); + ret = wps->wps->cred_cb(wps->wps->cb_ctx, &cred); } - return 0; + return ret; } diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c index 078ff72781a6f..b840acd924a74 100644 --- a/src/wps/wps_er.c +++ b/src/wps/wps_er.c @@ -1649,11 +1649,15 @@ static void wps_er_http_put_message_cb(void *ctx, struct http_client *c, case HTTP_CLIENT_OK: wpa_printf(MSG_DEBUG, "WPS ER: PutMessage OK"); reply = http_client_get_body(c); - if (reply == NULL) - break; - msg = os_zalloc(wpabuf_len(reply) + 1); - if (msg == NULL) + if (reply) + msg = os_zalloc(wpabuf_len(reply) + 1); + if (msg == NULL) { + if (ap->wps) { + wps_deinit(ap->wps); + ap->wps = NULL; + } break; + } os_memcpy(msg, wpabuf_head(reply), wpabuf_len(reply)); break; case HTTP_CLIENT_FAILED: @@ -1709,21 +1713,30 @@ static void wps_er_ap_put_message(struct wps_er_ap *ap, url = http_client_url_parse(ap->control_url, &dst, &path); if (url == NULL) { wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); - return; + goto fail; } buf = wps_er_soap_hdr(msg, "PutMessage", "NewInMessage", path, &dst, &len_ptr, &body_ptr); os_free(url); if (buf == NULL) - return; + goto fail; wps_er_soap_end(buf, "PutMessage", len_ptr, body_ptr); ap->http = http_client_addr(&dst, buf, 10000, wps_er_http_put_message_cb, ap); - if (ap->http == NULL) + if (ap->http == NULL) { wpabuf_free(buf); + goto fail; + } + return; + +fail: + if (ap->wps) { + wps_deinit(ap->wps); + ap->wps = NULL; + } } diff --git a/src/wps/wps_er_ssdp.c b/src/wps/wps_er_ssdp.c index e381fecbdc86a..280b2b3bde195 100644 --- a/src/wps/wps_er_ssdp.c +++ b/src/wps/wps_er_ssdp.c @@ -78,9 +78,7 @@ static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx) if (os_strstr(start, "ssdp:byebye")) byebye = 1; } else if (os_strncasecmp(start, "CACHE-CONTROL:", 14) == 0) { - start += 9; - while (*start == ' ') - start++; + start += 14; pos2 = os_strstr(start, "max-age="); if (pos2 == NULL) continue; diff --git a/src/wps/wps_module_tests.c b/src/wps/wps_module_tests.c index 6800e86db9119..350630768be4b 100644 --- a/src/wps/wps_module_tests.c +++ b/src/wps/wps_module_tests.c @@ -17,7 +17,7 @@ struct wps_attr_parse_test { int extra; }; -struct wps_attr_parse_test wps_attr_parse_test_cases[] = { +const struct wps_attr_parse_test wps_attr_parse_test_cases[] = { /* Empty message */ { "", 0, 0 }, /* Truncated attribute header */ @@ -271,7 +271,7 @@ static int wps_attr_parse_tests(void) for (i = 0; i < ARRAY_SIZE(wps_attr_parse_test_cases); i++) { struct wpabuf *buf; size_t len; - struct wps_attr_parse_test *test = + const struct wps_attr_parse_test *test = &wps_attr_parse_test_cases[i]; len = os_strlen(test->data) / 2; diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index 48b7e1288af08..4ca3a42d4c738 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -2605,13 +2605,16 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, token = wps_get_nfc_pw_token( &wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id); if (token && token->peer_pk_hash_known) { + size_t len; + wpa_printf(MSG_DEBUG, "WPS: Found matching NFC " "Password Token"); dl_list_del(&token->list); wps->nfc_pw_token = token; addr[0] = attr->public_key; - sha256_vector(1, addr, &attr->public_key_len, hash); + len = attr->public_key_len; + sha256_vector(1, addr, &len, hash); if (os_memcmp_const(hash, wps->nfc_pw_token->pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN) != 0) { @@ -3226,8 +3229,13 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, os_memset(&cred, 0, sizeof(cred)); os_memcpy(cred.ssid, wps->wps->ssid, wps->wps->ssid_len); cred.ssid_len = wps->wps->ssid_len; - cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK; - cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES; + if (wps->wps->rf_band_cb(wps->wps->cb_ctx) == WPS_RF_60GHZ) { + cred.auth_type = WPS_AUTH_WPA2PSK; + cred.encr_type = WPS_ENCR_AES; + } else { + cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK; + cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES; + } os_memcpy(cred.key, wps->new_psk, wps->new_psk_len); cred.key_len = wps->new_psk_len; diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c index 933d7340ee8ec..44318e0942520 100644 --- a/src/wps/wps_upnp.c +++ b/src/wps/wps_upnp.c @@ -695,6 +695,7 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, struct subscription *s; time_t now = time(NULL); time_t expire = now + UPNP_SUBSCRIBE_SEC; + char str[80]; /* Get rid of expired subscriptions so we have room */ subscription_list_age(sm, now); @@ -743,8 +744,10 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, subscription_destroy(s); return NULL; } - wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s", - s, callback_urls); + uuid_bin2str(s->uuid, str, sizeof(str)); + wpa_printf(MSG_DEBUG, + "WPS UPnP: Subscription %p (SID %s) started with %s", + s, str, callback_urls); /* Schedule sending this */ event_send_all_later(sm); return s; diff --git a/src/wps/wps_upnp_ap.c b/src/wps/wps_upnp_ap.c index 2949f141220b5..cca390530a169 100644 --- a/src/wps/wps_upnp_ap.c +++ b/src/wps/wps_upnp_ap.c @@ -34,10 +34,8 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg, wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes", msg); - if (wps_validate_upnp_set_selected_registrar(msg) < 0) - return -1; - - if (wps_parse_msg(msg, &attr) < 0) + if (wps_validate_upnp_set_selected_registrar(msg) < 0 || + wps_parse_msg(msg, &attr) < 0) return -1; s->reg = reg; diff --git a/src/wps/wps_upnp_event.c b/src/wps/wps_upnp_event.c index 2c8ed4f13996b..94aae7542b2e5 100644 --- a/src/wps/wps_upnp_event.c +++ b/src/wps/wps_upnp_event.c @@ -276,11 +276,9 @@ static int event_send_start(struct subscription *s) * Assume we are called ONLY with no current event and ONLY with * nonempty event queue and ONLY with at least one address to send to. */ - if (dl_list_empty(&s->addr_list)) - return -1; - if (s->current_event) - return -1; - if (dl_list_empty(&s->event_queue)) + if (dl_list_empty(&s->addr_list) || + s->current_event || + dl_list_empty(&s->event_queue)) return -1; s->current_event = e = event_dequeue(s); diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c index 26a740d252243..968fc03f92e7c 100644 --- a/src/wps/wps_upnp_ssdp.c +++ b/src/wps/wps_upnp_ssdp.c @@ -139,7 +139,7 @@ next_advertisement(struct upnp_wps_device_sm *sm, uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string)); msg = wpabuf_alloc(800); /* more than big enough */ if (msg == NULL) - goto fail; + return NULL; switch (a->type) { case ADVERTISE_UP: case ADVERTISE_DOWN: @@ -213,10 +213,6 @@ next_advertisement(struct upnp_wps_device_sm *sm, *islast = 1; return msg; - -fail: - wpabuf_free(msg); - return NULL; } @@ -744,11 +740,9 @@ int ssdp_listener_open(void) int sd; sd = socket(AF_INET, SOCK_DGRAM, 0); - if (sd < 0) - goto fail; - if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) - goto fail; - if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) + if (sd < 0 || + fcntl(sd, F_SETFL, O_NONBLOCK) != 0 || + setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) goto fail; os_memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; @@ -760,9 +754,8 @@ int ssdp_listener_open(void) mcast_addr.imr_interface.s_addr = htonl(INADDR_ANY); mcast_addr.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS); if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, - (char *) &mcast_addr, sizeof(mcast_addr))) - goto fail; - if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, + (char *) &mcast_addr, sizeof(mcast_addr)) || + setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) goto fail; diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c index b1cf571d8acb1..d5b0b5b26e9d5 100644 --- a/src/wps/wps_upnp_web.c +++ b/src/wps/wps_upnp_web.c @@ -1003,6 +1003,8 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm, ret = HTTP_INTERNAL_SERVER_ERROR; goto error; } + if (len > 0 && callback_urls[len - 1] == '\r') + callback_urls[len - 1] = '\0'; continue; } /* SID is only for renewal */ @@ -1214,18 +1216,25 @@ static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm, } if (got_uuid) { + char str[80]; + + uuid_bin2str(uuid, str, sizeof(str)); + s = subscription_find(sm, uuid); if (s) { struct subscr_addr *sa; sa = dl_list_first(&s->addr_list, struct subscr_addr, list); - wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s", - s, (sa && sa->domain_and_port) ? + wpa_printf(MSG_DEBUG, + "WPS UPnP: Unsubscribing %p (SID %s) %s", + s, str, (sa && sa->domain_and_port) ? sa->domain_and_port : "-null-"); dl_list_del(&s->list); subscription_destroy(s); } else { - wpa_printf(MSG_INFO, "WPS UPnP: Could not find matching subscription to unsubscribe"); + wpa_printf(MSG_INFO, + "WPS UPnP: Could not find matching subscription to unsubscribe (SID %s)", + str); ret = HTTP_PRECONDITION_FAILED; goto send_msg; } diff --git a/src/wps/wps_validate.c b/src/wps/wps_validate.c index 1c6a14bce4bcd..267b565e47847 100644 --- a/src/wps/wps_validate.c +++ b/src/wps/wps_validate.c @@ -224,6 +224,8 @@ static int wps_validate_rf_bands(const u8 *rf_bands, int mandatory) return 0; } if (*rf_bands != WPS_RF_24GHZ && *rf_bands != WPS_RF_50GHZ && + *rf_bands != WPS_RF_60GHZ && + *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ | WPS_RF_60GHZ) && *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ)) { wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Rf Bands " "attribute value 0x%x", *rf_bands); |