diff options
author | Cy Schubert <cy@FreeBSD.org> | 2019-04-22 15:42:53 +0000 |
---|---|---|
committer | Cy Schubert <cy@FreeBSD.org> | 2019-04-22 15:42:53 +0000 |
commit | 6e6d0eb51ef7b7487340bae7f20097ee5a57dbf4 (patch) | |
tree | 406310b03a08c8e00c863a82934ba6a8e33c6b2f /src | |
parent | 8a36c5c2ca4d1f8a900ca3d9ffde40b96463def7 (diff) |
Notes
Diffstat (limited to 'src')
204 files changed, 11489 insertions, 2783 deletions
diff --git a/src/ap/acs.c b/src/ap/acs.c index 6d4c0416dd425..3b4507575328a 100644 --- a/src/ap/acs.c +++ b/src/ap/acs.c @@ -13,6 +13,7 @@ #include "utils/common.h" #include "utils/list.h" #include "common/ieee802_11_defs.h" +#include "common/hw_features_common.h" #include "common/wpa_ctrl.h" #include "drivers/driver.h" #include "hostapd.h" @@ -362,7 +363,7 @@ acs_survey_chan_interference_factor(struct hostapd_iface *iface, } -static int acs_usable_ht40_chan(struct hostapd_channel_data *chan) +static int acs_usable_ht40_chan(const struct hostapd_channel_data *chan) { const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 184, 192 }; @@ -376,7 +377,7 @@ static int acs_usable_ht40_chan(struct hostapd_channel_data *chan) } -static int acs_usable_vht80_chan(struct hostapd_channel_data *chan) +static int acs_usable_vht80_chan(const struct hostapd_channel_data *chan) { const int allowed[] = { 36, 52, 100, 116, 132, 149 }; unsigned int i; @@ -389,6 +390,19 @@ static int acs_usable_vht80_chan(struct hostapd_channel_data *chan) } +static int acs_usable_vht160_chan(const struct hostapd_channel_data *chan) +{ + const int allowed[] = { 36, 100 }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(allowed); i++) + if (chan->chan == allowed[i]) + return 1; + + return 0; +} + + static int acs_survey_is_sufficient(struct freq_survey *survey) { if (!(survey->filled & SURVEY_HAS_NF)) { @@ -565,6 +579,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface) long double factor, ideal_factor = 0; int i, j; int n_chans = 1; + u32 bw; unsigned int k; /* TODO: HT40- support */ @@ -579,16 +594,23 @@ acs_find_ideal_chan(struct hostapd_iface *iface) iface->conf->secondary_channel) n_chans = 2; - if (iface->conf->ieee80211ac && - iface->conf->vht_oper_chwidth == 1) - n_chans = 4; + if (iface->conf->ieee80211ac) { + switch (iface->conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_80MHZ: + n_chans = 4; + break; + case VHT_CHANWIDTH_160MHZ: + n_chans = 8; + break; + } + } + + bw = num_chan_to_bw(n_chans); - /* TODO: VHT80+80, VHT160. Update acs_adjust_vht_center_freq() too. */ + /* TODO: VHT80+80. Update acs_adjust_vht_center_freq() too. */ - wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz", - n_chans == 1 ? 20 : - n_chans == 2 ? 40 : - 80); + wpa_printf(MSG_DEBUG, + "ACS: Survey analysis for selected bandwidth %d MHz", bw); for (i = 0; i < iface->current_mode->num_channels; i++) { double total_weight; @@ -596,12 +618,23 @@ acs_find_ideal_chan(struct hostapd_iface *iface) chan = &iface->current_mode->channels[i]; - if (chan->flag & HOSTAPD_CHAN_DISABLED) + /* Since in the current ACS implementation the first channel is + * always a primary channel, skip channels not available as + * primary until more sophisticated channel selection is + * implemented. */ + if (!chan_pri_allowed(chan)) continue; if (!is_in_chanlist(iface, chan)) continue; + if (!chan_bw_allowed(chan, bw, 1, 1)) { + wpa_printf(MSG_DEBUG, + "ACS: Channel %d: BW %u is not supported", + chan->chan, bw); + continue; + } + /* HT40 on 5 GHz has a limited set of primary channels as per * 11n Annex J */ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && @@ -614,12 +647,24 @@ acs_find_ideal_chan(struct hostapd_iface *iface) } if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && - iface->conf->ieee80211ac && - iface->conf->vht_oper_chwidth == 1 && - !acs_usable_vht80_chan(chan)) { - wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for VHT80", - chan->chan); - continue; + iface->conf->ieee80211ac) { + if (iface->conf->vht_oper_chwidth == + VHT_CHANWIDTH_80MHZ && + !acs_usable_vht80_chan(chan)) { + wpa_printf(MSG_DEBUG, + "ACS: Channel %d: not allowed as primary channel for VHT80", + chan->chan); + continue; + } + + if (iface->conf->vht_oper_chwidth == + VHT_CHANWIDTH_160MHZ && + !acs_usable_vht160_chan(chan)) { + wpa_printf(MSG_DEBUG, + "ACS: Channel %d: not allowed as primary channel for VHT160", + chan->chan); + continue; + } } factor = 0; @@ -632,6 +677,13 @@ acs_find_ideal_chan(struct hostapd_iface *iface) if (!adj_chan) break; + if (!chan_bw_allowed(adj_chan, bw, 1, 0)) { + wpa_printf(MSG_DEBUG, + "ACS: PRI Channel %d: secondary channel %d BW %u is not supported", + chan->chan, adj_chan->chan, bw); + break; + } + if (acs_usable_chan(adj_chan)) { factor += adj_chan->interference_factor; total_weight += 1; @@ -744,10 +796,14 @@ static void acs_adjust_vht_center_freq(struct hostapd_iface *iface) case VHT_CHANWIDTH_80MHZ: offset = 6; break; + case VHT_CHANWIDTH_160MHZ: + offset = 14; + break; default: /* TODO: How can this be calculated? Adjust * acs_find_ideal_chan() */ - wpa_printf(MSG_INFO, "ACS: Only VHT20/40/80 is supported now"); + wpa_printf(MSG_INFO, + "ACS: Only VHT20/40/80/160 is supported now"); return; } diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index f9b6f295927f9..e640e9984b70e 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -131,6 +131,15 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) * This can be enabled by default once the implementation has been fully * completed and tested with other implementations. */ bss->tls_flags = TLS_CONN_DISABLE_TLSv1_3; + + bss->send_probe_response = 1; + +#ifdef CONFIG_HS20 + bss->hs20_release = (HS20_VERSION >> 4) + 1; +#endif /* CONFIG_HS20 */ + + /* Default to strict CRL checking. */ + bss->check_crl_strict = 1; } @@ -191,9 +200,8 @@ struct hostapd_config * hostapd_config_defaults(void) conf->num_bss = 1; conf->beacon_int = 100; - conf->rts_threshold = -1; /* use driver default: 2347 */ - conf->fragm_threshold = -1; /* user driver default: 2346 */ - conf->send_probe_response = 1; + conf->rts_threshold = -2; /* use driver default: 2347 */ + conf->fragm_threshold = -2; /* user driver default: 2346 */ /* Set to invalid value means do not add Power Constraint IE */ conf->local_pwr_constraint = -1; @@ -233,6 +241,9 @@ struct hostapd_config * hostapd_config_defaults(void) * environments for the current frequency band in the country. */ conf->country[2] = ' '; + conf->rssi_reject_assoc_rssi = 0; + conf->rssi_reject_assoc_timeout = 30; + return conf; } @@ -248,6 +259,12 @@ static int hostapd_config_read_wpa_psk(const char *fname, { FILE *f; char buf[128], *pos; + const char *keyid; + char *context; + char *context2; + char *token; + char *name; + char *value; int line = 0, ret = 0, len, ok; u8 addr[ETH_ALEN]; struct hostapd_wpa_psk *psk; @@ -262,6 +279,8 @@ static int hostapd_config_read_wpa_psk(const char *fname, } while (fgets(buf, sizeof(buf), f)) { + int vlan_id = 0; + line++; if (buf[0] == '#') @@ -277,9 +296,39 @@ static int hostapd_config_read_wpa_psk(const char *fname, if (buf[0] == '\0') continue; - if (hwaddr_aton(buf, addr)) { + context = NULL; + keyid = NULL; + while ((token = str_token(buf, " ", &context))) { + if (!os_strchr(token, '=')) + break; + context2 = NULL; + name = str_token(token, "=", &context2); + if (!name) + break; + value = str_token(token, "", &context2); + if (!value) + value = ""; + if (!os_strcmp(name, "keyid")) { + keyid = value; + } else if (!os_strcmp(name, "vlanid")) { + vlan_id = atoi(value); + } else { + wpa_printf(MSG_ERROR, + "Unrecognized '%s=%s' on line %d in '%s'", + name, value, line, fname); + ret = -1; + break; + } + } + + if (ret == -1) + break; + + if (!token) + token = ""; + if (hwaddr_aton(token, addr)) { wpa_printf(MSG_ERROR, "Invalid MAC address '%s' on " - "line %d in '%s'", buf, line, fname); + "line %d in '%s'", token, line, fname); ret = -1; break; } @@ -290,20 +339,20 @@ static int hostapd_config_read_wpa_psk(const char *fname, ret = -1; break; } + psk->vlan_id = vlan_id; if (is_zero_ether_addr(addr)) psk->group = 1; else os_memcpy(psk->addr, addr, ETH_ALEN); - pos = buf + 17; - if (*pos == '\0') { + pos = str_token(buf, "", &context); + if (!pos) { wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'", line, fname); os_free(psk); ret = -1; break; } - pos++; ok = 0; len = os_strlen(pos); @@ -322,6 +371,18 @@ static int hostapd_config_read_wpa_psk(const char *fname, break; } + if (keyid) { + len = os_strlcpy(psk->keyid, keyid, sizeof(psk->keyid)); + if ((size_t) len >= sizeof(psk->keyid)) { + wpa_printf(MSG_ERROR, + "PSK keyid too long on line %d in '%s'", + line, fname); + os_free(psk); + ret = -1; + break; + } + } + psk->next = ssid->wpa_psk; ssid->wpa_psk = psk; } @@ -534,10 +595,12 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->server_cert); os_free(conf->private_key); os_free(conf->private_key_passwd); + os_free(conf->check_cert_subject); os_free(conf->ocsp_stapling_response); os_free(conf->ocsp_stapling_response_multi); os_free(conf->dh_file); os_free(conf->openssl_ciphers); + os_free(conf->openssl_ecdh_curves); os_free(conf->pac_opaque_encr_key); os_free(conf->eap_fast_a_id); os_free(conf->eap_fast_a_id_info); @@ -582,6 +645,8 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->ap_pin); os_free(conf->extra_cred); os_free(conf->ap_settings); + hostapd_config_clear_wpa_psk(&conf->multi_ap_backhaul_ssid.wpa_psk); + str_clear_free(conf->multi_ap_backhaul_ssid.wpa_passphrase); os_free(conf->upnp_iface); os_free(conf->friendly_name); os_free(conf->manufacturer_url); @@ -644,6 +709,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->hs20_operator_icon); } os_free(conf->subscr_remediation_url); + os_free(conf->hs20_sim_provisioning_url); os_free(conf->t_c_filename); os_free(conf->t_c_server_url); #endif /* CONFIG_HS20 */ @@ -802,11 +868,14 @@ const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id) const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, const u8 *addr, const u8 *p2p_dev_addr, - const u8 *prev_psk) + const u8 *prev_psk, int *vlan_id) { struct hostapd_wpa_psk *psk; int next_ok = prev_psk == NULL; + if (vlan_id) + *vlan_id = 0; + if (p2p_dev_addr && !is_zero_ether_addr(p2p_dev_addr)) { wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR " p2p_dev_addr=" MACSTR " prev_psk=%p", @@ -824,8 +893,11 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, (addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) || (!addr && p2p_dev_addr && os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) == - 0))) + 0))) { + if (vlan_id) + *vlan_id = psk->vlan_id; return psk->psk; + } if (psk->psk == prev_psk) next_ok = 1; @@ -1003,6 +1075,15 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, } #endif /* CONFIG_MBO */ +#ifdef CONFIG_OCV + if (full_config && bss->ieee80211w == NO_MGMT_FRAME_PROTECTION && + bss->ocv) { + wpa_printf(MSG_ERROR, + "OCV: PMF needs to be enabled whenever using OCV"); + return -1; + } +#endif /* CONFIG_OCV */ + return 0; } @@ -1146,3 +1227,26 @@ void hostapd_set_security_params(struct hostapd_bss_config *bss, } } } + + +int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf) +{ + int with_id = 0, without_id = 0; + struct sae_password_entry *pw; + + if (conf->ssid.wpa_passphrase) + without_id = 1; + + for (pw = conf->sae_passwords; pw; pw = pw->next) { + if (pw->identifier) + with_id = 1; + else + without_id = 1; + if (with_id && without_id) + break; + } + + if (with_id && !without_id) + return 2; + return with_id; +} diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 778366d49afe0..509677a45f056 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -42,6 +42,7 @@ struct mesh_conf { #define MESH_CONF_SEC_AMPE BIT(2) unsigned int security; enum mfp_options ieee80211w; + int ocv; unsigned int pairwise_cipher; unsigned int group_cipher; unsigned int mgmt_group_cipher; @@ -122,6 +123,7 @@ struct hostapd_vlan { int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */ struct vlan_description vlan_desc; char ifname[IFNAMSIZ + 1]; + char bridge[IFNAMSIZ + 1]; int configured; int dynamic_vlan; #ifdef CONFIG_FULL_DYNAMIC_VLAN @@ -132,6 +134,7 @@ struct hostapd_vlan { }; #define PMK_LEN 32 +#define KEYID_LEN 32 #define MIN_PASSPHRASE_LEN 8 #define MAX_PASSPHRASE_LEN 63 struct hostapd_sta_wpa_psk_short { @@ -145,9 +148,11 @@ struct hostapd_sta_wpa_psk_short { struct hostapd_wpa_psk { struct hostapd_wpa_psk *next; int group; + char keyid[KEYID_LEN]; u8 psk[PMK_LEN]; u8 addr[ETH_ALEN]; u8 p2p_dev_addr[ETH_ALEN]; + int vlan_id; }; struct hostapd_eap_user { @@ -244,6 +249,7 @@ struct sae_password_entry { char *password; char *identifier; u8 peer_addr[ETH_ALEN]; + int vlan_id; }; /** @@ -335,6 +341,9 @@ struct hostapd_bss_config { /* dot11AssociationSAQueryRetryTimeout (in TUs) */ int assoc_sa_query_retry_timeout; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + int ocv; /* Operating Channel Validation */ +#endif /* CONFIG_OCV */ enum { PSK_RADIUS_IGNORED = 0, PSK_RADIUS_ACCEPTED = 1, @@ -383,13 +392,17 @@ struct hostapd_bss_config { char *server_cert; char *private_key; char *private_key_passwd; + char *check_cert_subject; int check_crl; + int check_crl_strict; + unsigned int crl_reload_interval; unsigned int tls_session_lifetime; unsigned int tls_flags; char *ocsp_stapling_response; char *ocsp_stapling_response_multi; char *dh_file; char *openssl_ciphers; + char *openssl_ecdh_curves; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; @@ -452,9 +465,11 @@ struct hostapd_bss_config { u8 *extra_cred; size_t extra_cred_len; int wps_cred_processing; + int wps_cred_add_sae; int force_per_enrollee_psk; u8 *ap_settings; size_t ap_settings_len; + struct hostapd_ssid multi_ap_backhaul_ssid; char *upnp_iface; char *friendly_name; char *manufacturer_url; @@ -557,6 +572,7 @@ struct hostapd_bss_config { int na_mcast_to_ucast; #ifdef CONFIG_HS20 int hs20; + int hs20_release; int disable_dgaf; u16 anqp_domain_id; unsigned int hs20_oper_friendly_name_count; @@ -596,6 +612,7 @@ struct hostapd_bss_config { unsigned int hs20_deauth_req_timeout; char *subscr_remediation_url; u8 subscr_remediation_method; + char *hs20_sim_provisioning_url; char *t_c_filename; u32 t_c_timestamp; char *t_c_server_url; @@ -686,6 +703,12 @@ struct hostapd_bss_config { #endif /* CONFIG_OWE */ int coloc_intf_reporting; + + u8 send_probe_response; + +#define BACKHAUL_BSS 1 +#define FRONTHAUL_BSS 2 + int multi_ap; /* bitmap of BACKHAUL_BSS, FRONTHAUL_BSS */ }; /** @@ -717,7 +740,6 @@ struct hostapd_config { u16 beacon_int; int rts_threshold; int fragm_threshold; - u8 send_probe_response; u8 channel; u8 acs; struct wpa_freq_range_list acs_ch_list; @@ -829,12 +851,16 @@ struct hostapd_config { #ifdef CONFIG_IEEE80211AX struct he_phy_capabilities_info he_phy_capab; struct he_operation he_op; + struct ieee80211_he_mu_edca_parameter_set he_mu_edca; #endif /* CONFIG_IEEE80211AX */ /* VHT enable/disable config from CHAN_SWITCH */ #define CH_SWITCH_VHT_ENABLED BIT(0) #define CH_SWITCH_VHT_DISABLED BIT(1) unsigned int ch_switch_vht_config; + + int rssi_reject_assoc_rssi; + int rssi_reject_assoc_timeout; }; @@ -851,7 +877,7 @@ int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, int hostapd_rate_found(int *list, int rate); const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, const u8 *addr, const u8 *p2p_dev_addr, - const u8 *prev_psk); + const u8 *prev_psk, int *vlan_id); int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf); int hostapd_vlan_valid(struct hostapd_vlan *vlan, struct vlan_description *vlan_desc); @@ -862,5 +888,6 @@ hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type); int hostapd_config_check(struct hostapd_config *conf, int full_config); void hostapd_set_security_params(struct hostapd_bss_config *bss, int full_config); +int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf); #endif /* HOSTAPD_CONFIG_H */ diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index db93fde7d6e3a..de40171e18dcb 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -356,4 +356,22 @@ static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd) return hapd->driver->stop_ap(hapd->drv_priv); } +static inline int hostapd_drv_channel_info(struct hostapd_data *hapd, + struct wpa_channel_info *ci) +{ + if (!hapd->driver || !hapd->driver->channel_info) + return -1; + return hapd->driver->channel_info(hapd->drv_priv, ci); +} + +static inline int +hostapd_drv_send_external_auth_status(struct hostapd_data *hapd, + struct external_auth *params) +{ + if (!hapd->driver || !hapd->drv_priv || + !hapd->driver->send_external_auth_status) + return -1; + return hapd->driver->send_external_auth_status(hapd->drv_priv, params); +} + #endif /* AP_DRV_OPS */ diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index 95d004ed2b16a..eced6c7c6d941 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -136,6 +136,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) #ifdef CONFIG_HS20 srv.subscr_remediation_url = conf->subscr_remediation_url; srv.subscr_remediation_method = conf->subscr_remediation_method; + srv.hs20_sim_provisioning_url = conf->hs20_sim_provisioning_url; srv.t_c_server_url = conf->t_c_server_url; #endif /* CONFIG_HS20 */ srv.erp = conf->eap_server_erp; @@ -200,6 +201,16 @@ int authsrv_init(struct hostapd_data *hapd) os_memset(&conf, 0, sizeof(conf)); conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; + if (hapd->conf->crl_reload_interval > 0 && + hapd->conf->check_crl <= 0) { + wpa_printf(MSG_INFO, + "Cannot enable CRL reload functionality - it depends on check_crl being set"); + } else if (hapd->conf->crl_reload_interval > 0) { + conf.crl_reload_interval = + hapd->conf->crl_reload_interval; + wpa_printf(MSG_INFO, + "Enabled CRL reload functionality"); + } conf.tls_flags = hapd->conf->tls_flags; conf.event_cb = authsrv_tls_event; conf.cb_ctx = hapd; @@ -217,10 +228,12 @@ int authsrv_init(struct hostapd_data *hapd) params.private_key_passwd = hapd->conf->private_key_passwd; params.dh_file = hapd->conf->dh_file; params.openssl_ciphers = hapd->conf->openssl_ciphers; + params.openssl_ecdh_curves = hapd->conf->openssl_ecdh_curves; params.ocsp_stapling_response = hapd->conf->ocsp_stapling_response; params.ocsp_stapling_response_multi = hapd->conf->ocsp_stapling_response_multi; + params.check_cert_subject = hapd->conf->check_cert_subject; if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) { wpa_printf(MSG_ERROR, "Failed to set TLS parameters"); @@ -229,7 +242,8 @@ int authsrv_init(struct hostapd_data *hapd) } if (tls_global_set_verify(hapd->ssl_ctx, - hapd->conf->check_crl)) { + hapd->conf->check_crl, + hapd->conf->check_crl_strict)) { wpa_printf(MSG_ERROR, "Failed to enable check_crl"); authsrv_deinit(hapd); return -1; diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 59bd4af395d7a..3e62991d07aff 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -397,7 +397,8 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211AX if (hapd->iconf->ieee80211ax) { buflen += 3 + sizeof(struct ieee80211_he_capabilities) + - 3 + sizeof(struct ieee80211_he_operation); + 3 + sizeof(struct ieee80211_he_operation) + + 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set); } #endif /* CONFIG_IEEE80211AX */ @@ -510,6 +511,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, if (hapd->iconf->ieee80211ax) { pos = hostapd_eid_he_capab(hapd, pos); pos = hostapd_eid_he_operation(hapd, pos); + pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos); } #endif /* CONFIG_IEEE80211AX */ @@ -767,7 +769,7 @@ void handle_probe_req(struct hostapd_data *hapd, ie, ie_len, ssi_signal) > 0) return; - if (!hapd->iconf->send_probe_response) + if (!hapd->conf->send_probe_response) return; if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) { @@ -1085,7 +1087,8 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211AX if (hapd->iconf->ieee80211ax) { tail_len += 3 + sizeof(struct ieee80211_he_capabilities) + - 3 + sizeof(struct ieee80211_he_operation); + 3 + sizeof(struct ieee80211_he_operation) + + 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set); } #endif /* CONFIG_IEEE80211AX */ @@ -1222,6 +1225,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, if (hapd->iconf->ieee80211ax) { tailpos = hostapd_eid_he_capab(hapd, tailpos); tailpos = hostapd_eid_he_operation(hapd, tailpos); + tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos); } #endif /* CONFIG_IEEE80211AX */ @@ -1357,6 +1361,18 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #endif /* CONFIG_HS20 */ params->multicast_to_unicast = hapd->conf->multicast_to_unicast; params->pbss = hapd->conf->pbss; + + if (hapd->conf->ftm_responder) { + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_FTM_RESPONDER) { + params->ftm_responder = 1; + params->lci = hapd->iface->conf->lci; + params->civic = hapd->iface->conf->civic; + } else { + wpa_printf(MSG_WARNING, + "Not configuring FTM responder as the driver doesn't advertise support for it"); + } + } + return 0; } diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index 21b813ee18d77..c693715116344 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -1,6 +1,6 @@ /* * Control interface for shared AP commands - * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -207,6 +207,7 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, char *buf, size_t buflen) { int len, res, ret, i; + const char *keyid; if (!sta) return 0; @@ -341,6 +342,13 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, len += ret; } + keyid = ap_sta_wpa_get_keyid(hapd, sta); + if (keyid) { + ret = os_snprintf(buf + len, buflen - len, "keyid=%s\n", keyid); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + return len; } @@ -443,11 +451,11 @@ static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, if (stype == WLAN_FC_STYPE_DEAUTH) { mgmt->u.deauth.reason_code = host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); - pos = (u8 *) (&mgmt->u.deauth.reason_code + 1); + pos = mgmt->u.deauth.variable; } else { mgmt->u.disassoc.reason_code = host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); - pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1); + pos = mgmt->u.disassoc.variable; } *pos++ = WLAN_EID_VENDOR_SPECIFIC; diff --git a/src/ap/dfs.c b/src/ap/dfs.c index 993dd19c2cb07..79cd00f44a780 100644 --- a/src/ap/dfs.c +++ b/src/ap/dfs.c @@ -142,18 +142,30 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode, { struct hostapd_channel_data *first_chan, *chan; int i; + u32 bw = num_chan_to_bw(num_chans); if (first_chan_idx + num_chans > mode->num_channels) return 0; first_chan = &mode->channels[first_chan_idx]; + /* hostapd DFS implementation assumes the first channel as primary. + * If it's not allowed to use the first channel as primary, decline the + * whole channel range. */ + if (!chan_pri_allowed(first_chan)) + return 0; + for (i = 0; i < num_chans; i++) { chan = dfs_get_chan_data(mode, first_chan->freq + i * 20, first_chan_idx); if (!chan) return 0; + /* HT 40 MHz secondary channel availability checked only for + * primary channel */ + if (!chan_bw_allowed(chan, bw, 1, !i)) + return 0; + if (!dfs_channel_available(chan, skip_radar)) return 0; } @@ -197,7 +209,8 @@ static int dfs_find_channel(struct hostapd_iface *iface, /* Skip HT40/VHT incompatible channels */ if (iface->conf->ieee80211n && iface->conf->secondary_channel && - !dfs_is_chan_allowed(chan, n_chans)) + (!dfs_is_chan_allowed(chan, n_chans) || + !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))) continue; /* Skip incompatible chandefs */ diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c index 6d8c2f4be0da9..ed37fc8fe96a0 100644 --- a/src/ap/dhcp_snoop.c +++ b/src/ap/dhcp_snoop.c @@ -88,6 +88,15 @@ static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf, } } + if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) { + 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 (msgtype == DHCPACK) { if (b->your_ip == 0) return; @@ -124,15 +133,6 @@ static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf, } sta->ipaddr = b->your_ip; } - - if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) { - 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); - } - } } diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c index 149f389f789f1..75edbc909e7a0 100644 --- a/src/ap/dpp_hostapd.c +++ b/src/ap/dpp_hostapd.c @@ -28,34 +28,6 @@ static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd); static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; -static struct dpp_configurator * -hostapd_dpp_configurator_get_id(struct hostapd_data *hapd, unsigned int id) -{ - struct dpp_configurator *conf; - - dl_list_for_each(conf, &hapd->iface->interfaces->dpp_configurator, - struct dpp_configurator, list) { - if (conf->id == id) - return conf; - } - return NULL; -} - - -static unsigned int hapd_dpp_next_id(struct hostapd_data *hapd) -{ - struct dpp_bootstrap_info *bi; - unsigned int max_id = 0; - - dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap, - struct dpp_bootstrap_info, list) { - if (bi->id > max_id) - max_id = bi->id; - } - return max_id + 1; -} - - /** * hostapd_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code * @hapd: Pointer to hostapd_data @@ -67,13 +39,10 @@ int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd) struct dpp_bootstrap_info *bi; struct dpp_authentication *auth = hapd->dpp_auth; - bi = dpp_parse_qr_code(cmd); + bi = dpp_add_qr_code(hapd->iface->interfaces->dpp, cmd); if (!bi) return -1; - bi->id = hapd_dpp_next_id(hapd); - dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list); - if (auth && auth->response_pending && dpp_notify_new_qr_code(auth, bi) == 1) { wpa_printf(MSG_DEBUG, @@ -92,195 +61,6 @@ int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd) } -static char * get_param(const char *cmd, const char *param) -{ - const char *pos, *end; - char *val; - size_t len; - - pos = os_strstr(cmd, param); - if (!pos) - return NULL; - - pos += os_strlen(param); - end = os_strchr(pos, ' '); - if (end) - len = end - pos; - else - len = os_strlen(pos); - val = os_malloc(len + 1); - if (!val) - return NULL; - os_memcpy(val, pos, len); - val[len] = '\0'; - return val; -} - - -int hostapd_dpp_bootstrap_gen(struct hostapd_data *hapd, const char *cmd) -{ - char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL; - char *key = NULL; - u8 *privkey = NULL; - size_t privkey_len = 0; - size_t len; - int ret = -1; - struct dpp_bootstrap_info *bi; - - bi = os_zalloc(sizeof(*bi)); - if (!bi) - goto fail; - - if (os_strstr(cmd, "type=qrcode")) - bi->type = DPP_BOOTSTRAP_QR_CODE; - else if (os_strstr(cmd, "type=pkex")) - bi->type = DPP_BOOTSTRAP_PKEX; - else - goto fail; - - chan = get_param(cmd, " chan="); - mac = get_param(cmd, " mac="); - info = get_param(cmd, " info="); - curve = get_param(cmd, " curve="); - key = get_param(cmd, " key="); - - if (key) { - privkey_len = os_strlen(key) / 2; - privkey = os_malloc(privkey_len); - if (!privkey || - hexstr2bin(key, privkey, privkey_len) < 0) - goto fail; - } - - pk = dpp_keygen(bi, curve, privkey, privkey_len); - if (!pk) - goto fail; - - len = 4; /* "DPP:" */ - if (chan) { - if (dpp_parse_uri_chan_list(bi, chan) < 0) - goto fail; - len += 3 + os_strlen(chan); /* C:...; */ - } - if (mac) { - if (dpp_parse_uri_mac(bi, mac) < 0) - goto fail; - len += 3 + os_strlen(mac); /* M:...; */ - } - if (info) { - if (dpp_parse_uri_info(bi, info) < 0) - goto fail; - len += 3 + os_strlen(info); /* I:...; */ - } - len += 4 + os_strlen(pk); - bi->uri = os_malloc(len + 1); - if (!bi->uri) - goto fail; - os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;", - chan ? "C:" : "", chan ? chan : "", chan ? ";" : "", - mac ? "M:" : "", mac ? mac : "", mac ? ";" : "", - info ? "I:" : "", info ? info : "", info ? ";" : "", - pk); - bi->id = hapd_dpp_next_id(hapd); - dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list); - ret = bi->id; - bi = NULL; -fail: - os_free(curve); - os_free(pk); - os_free(chan); - os_free(mac); - os_free(info); - str_clear_free(key); - bin_clear_free(privkey, privkey_len); - dpp_bootstrap_info_free(bi); - return ret; -} - - -static struct dpp_bootstrap_info * -dpp_bootstrap_get_id(struct hostapd_data *hapd, unsigned int id) -{ - struct dpp_bootstrap_info *bi; - - dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap, - struct dpp_bootstrap_info, list) { - if (bi->id == id) - return bi; - } - return NULL; -} - - -static int dpp_bootstrap_del(struct hapd_interfaces *ifaces, unsigned int id) -{ - struct dpp_bootstrap_info *bi, *tmp; - int found = 0; - - dl_list_for_each_safe(bi, tmp, &ifaces->dpp_bootstrap, - struct dpp_bootstrap_info, list) { - if (id && bi->id != id) - continue; - found = 1; - dl_list_del(&bi->list); - dpp_bootstrap_info_free(bi); - } - - if (id == 0) - return 0; /* flush succeeds regardless of entries found */ - return found ? 0 : -1; -} - - -int hostapd_dpp_bootstrap_remove(struct hostapd_data *hapd, const char *id) -{ - unsigned int id_val; - - if (os_strcmp(id, "*") == 0) { - id_val = 0; - } else { - id_val = atoi(id); - if (id_val == 0) - return -1; - } - - return dpp_bootstrap_del(hapd->iface->interfaces, id_val); -} - - -const char * hostapd_dpp_bootstrap_get_uri(struct hostapd_data *hapd, - unsigned int id) -{ - struct dpp_bootstrap_info *bi; - - bi = dpp_bootstrap_get_id(hapd, id); - if (!bi) - return NULL; - return bi->uri; -} - - -int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id, - char *reply, int reply_size) -{ - struct dpp_bootstrap_info *bi; - - bi = dpp_bootstrap_get_id(hapd, id); - if (!bi) - return -1; - return os_snprintf(reply, reply_size, "type=%s\n" - "mac_addr=" MACSTR "\n" - "info=%s\n" - "num_freq=%u\n" - "curve=%s\n", - dpp_bootstrap_type_txt(bi->type), - MAC2STR(bi->mac_addr), - bi->info ? bi->info : "", - bi->num_freq, - bi->curve->name); -} - - static void hostapd_dpp_auth_resp_retry_timeout(void *eloop_ctx, void *timeout_ctx) { @@ -354,6 +134,16 @@ void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst, return; } +#ifdef CONFIG_DPP2 + if (auth->connect_on_tx_status) { + wpa_printf(MSG_DEBUG, + "DPP: Complete exchange on configuration result"); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + return; + } +#endif /* CONFIG_DPP2 */ + if (hapd->dpp_auth->remove_on_tx_status) { wpa_printf(MSG_DEBUG, "DPP: Terminate authentication exchange due to an earlier error"); @@ -505,178 +295,6 @@ static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd, } -static int hostapd_dpp_set_configurator(struct hostapd_data *hapd, - struct dpp_authentication *auth, - const char *cmd) -{ - const char *pos, *end; - struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL; - struct dpp_configurator *conf = NULL; - u8 ssid[32] = { "test" }; - size_t ssid_len = 4; - char pass[64] = { }; - size_t pass_len = 0; - u8 psk[PMK_LEN]; - int psk_set = 0; - char *group_id = NULL; - - if (!cmd) - return 0; - - wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd); - pos = os_strstr(cmd, " ssid="); - if (pos) { - pos += 6; - end = os_strchr(pos, ' '); - ssid_len = end ? (size_t) (end - pos) : os_strlen(pos); - ssid_len /= 2; - if (ssid_len > sizeof(ssid) || - hexstr2bin(pos, ssid, ssid_len) < 0) - goto fail; - } - - pos = os_strstr(cmd, " pass="); - if (pos) { - pos += 6; - end = os_strchr(pos, ' '); - pass_len = end ? (size_t) (end - pos) : os_strlen(pos); - pass_len /= 2; - if (pass_len > sizeof(pass) - 1 || pass_len < 8 || - hexstr2bin(pos, (u8 *) pass, pass_len) < 0) - goto fail; - } - - pos = os_strstr(cmd, " psk="); - if (pos) { - pos += 5; - if (hexstr2bin(pos, psk, PMK_LEN) < 0) - goto fail; - psk_set = 1; - } - - pos = os_strstr(cmd, " group_id="); - if (pos) { - size_t group_id_len; - - pos += 10; - end = os_strchr(pos, ' '); - group_id_len = end ? (size_t) (end - pos) : os_strlen(pos); - group_id = os_malloc(group_id_len + 1); - if (!group_id) - goto fail; - os_memcpy(group_id, pos, group_id_len); - group_id[group_id_len] = '\0'; - } - - if (os_strstr(cmd, " conf=sta-")) { - conf_sta = os_zalloc(sizeof(struct dpp_configuration)); - if (!conf_sta) - goto fail; - os_memcpy(conf_sta->ssid, ssid, ssid_len); - conf_sta->ssid_len = ssid_len; - if (os_strstr(cmd, " conf=sta-psk") || - os_strstr(cmd, " conf=sta-sae") || - os_strstr(cmd, " conf=sta-psk-sae")) { - if (os_strstr(cmd, " conf=sta-psk-sae")) - conf_sta->akm = DPP_AKM_PSK_SAE; - else if (os_strstr(cmd, " conf=sta-sae")) - conf_sta->akm = DPP_AKM_SAE; - else - conf_sta->akm = DPP_AKM_PSK; - if (psk_set) { - os_memcpy(conf_sta->psk, psk, PMK_LEN); - } else { - conf_sta->passphrase = os_strdup(pass); - if (!conf_sta->passphrase) - goto fail; - } - } else if (os_strstr(cmd, " conf=sta-dpp")) { - conf_sta->akm = DPP_AKM_DPP; - } else { - goto fail; - } - if (os_strstr(cmd, " group_id=")) { - conf_sta->group_id = group_id; - group_id = NULL; - } - } - - if (os_strstr(cmd, " conf=ap-")) { - conf_ap = os_zalloc(sizeof(struct dpp_configuration)); - if (!conf_ap) - goto fail; - os_memcpy(conf_ap->ssid, ssid, ssid_len); - conf_ap->ssid_len = ssid_len; - if (os_strstr(cmd, " conf=ap-psk") || - os_strstr(cmd, " conf=ap-sae") || - os_strstr(cmd, " conf=ap-psk-sae")) { - if (os_strstr(cmd, " conf=ap-psk-sae")) - conf_ap->akm = DPP_AKM_PSK_SAE; - else if (os_strstr(cmd, " conf=ap-sae")) - conf_ap->akm = DPP_AKM_SAE; - else - conf_ap->akm = DPP_AKM_PSK; - if (psk_set) { - os_memcpy(conf_ap->psk, psk, PMK_LEN); - } else if (pass_len > 0) { - conf_ap->passphrase = os_strdup(pass); - if (!conf_ap->passphrase) - goto fail; - } else { - goto fail; - } - } else if (os_strstr(cmd, " conf=ap-dpp")) { - conf_ap->akm = DPP_AKM_DPP; - } else { - goto fail; - } - if (os_strstr(cmd, " group_id=")) { - conf_ap->group_id = group_id; - group_id = NULL; - } - } - - pos = os_strstr(cmd, " expiry="); - if (pos) { - long int val; - - pos += 8; - val = strtol(pos, NULL, 0); - if (val <= 0) - goto fail; - if (conf_sta) - conf_sta->netaccesskey_expiry = val; - if (conf_ap) - conf_ap->netaccesskey_expiry = val; - } - - pos = os_strstr(cmd, " configurator="); - if (pos) { - auth->configurator = 1; - pos += 14; - conf = hostapd_dpp_configurator_get_id(hapd, atoi(pos)); - if (!conf) { - wpa_printf(MSG_INFO, - "DPP: Could not find the specified configurator"); - goto fail; - } - } - auth->conf_sta = conf_sta; - auth->conf_ap = conf_ap; - auth->conf = conf; - os_free(group_id); - return 0; - -fail: - wpa_msg(hapd->msg_ctx, MSG_INFO, - "DPP: Failed to set configurator parameters"); - dpp_configuration_free(conf_sta); - dpp_configuration_free(conf_ap); - os_free(group_id); - return -1; -} - - static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; @@ -786,7 +404,7 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd) if (!pos) return -1; pos += 6; - peer_bi = dpp_bootstrap_get_id(hapd, atoi(pos)); + peer_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos)); if (!peer_bi) { wpa_printf(MSG_INFO, "DPP: Could not find bootstrapping info for the identified peer"); @@ -796,7 +414,8 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd) pos = os_strstr(cmd, " own="); if (pos) { pos += 5; - own_bi = dpp_bootstrap_get_id(hapd, atoi(pos)); + own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, + atoi(pos)); if (!own_bi) { wpa_printf(MSG_INFO, "DPP: Could not find bootstrapping info for the identified local entry"); @@ -846,7 +465,8 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd) if (!hapd->dpp_auth) goto fail; hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth); - if (hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, cmd) < 0) { + if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx, + hapd->dpp_auth, cmd) < 0) { dpp_auth_deinit(hapd->dpp_auth); hapd->dpp_auth = NULL; goto fail; @@ -905,7 +525,10 @@ static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src, { const u8 *r_bootstrap, *i_bootstrap; u16 r_bootstrap_len, i_bootstrap_len; - struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL; + struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL; + + if (!hapd->iface->interfaces->dpp) + return; wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR, MAC2STR(src)); @@ -932,28 +555,8 @@ static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src, /* Try to find own and peer bootstrapping key matches based on the * received hash values */ - dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap, - struct dpp_bootstrap_info, list) { - if (!own_bi && bi->own && - os_memcmp(bi->pubkey_hash, r_bootstrap, - SHA256_MAC_LEN) == 0) { - wpa_printf(MSG_DEBUG, - "DPP: Found matching own bootstrapping information"); - own_bi = bi; - } - - if (!peer_bi && !bi->own && - os_memcmp(bi->pubkey_hash, i_bootstrap, - SHA256_MAC_LEN) == 0) { - wpa_printf(MSG_DEBUG, - "DPP: Found matching peer bootstrapping information"); - peer_bi = bi; - } - - if (own_bi && peer_bi) - break; - } - + dpp_bootstrap_find_pair(hapd->iface->interfaces->dpp, i_bootstrap, + r_bootstrap, &own_bi, &peer_bi); if (!own_bi) { wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "No matching own bootstrapping key found - ignore message"); @@ -975,8 +578,9 @@ static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src, return; } hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth); - if (hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, - hapd->dpp_configurator_params) < 0) { + if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx, + hapd->dpp_auth, + hapd->dpp_configurator_params) < 0) { dpp_auth_deinit(hapd->dpp_auth); hapd->dpp_auth = NULL; return; @@ -1072,6 +676,7 @@ static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, struct hostapd_data *hapd = ctx; const u8 *pos; struct dpp_authentication *auth = hapd->dpp_auth; + enum dpp_status_error status = DPP_STATUS_CONFIG_REJECTED; if (!auth || !auth->auth_success) { wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); @@ -1107,12 +712,41 @@ static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, } hostapd_dpp_handle_config_obj(hapd, auth); - dpp_auth_deinit(hapd->dpp_auth); - hapd->dpp_auth = NULL; - return; - + status = DPP_STATUS_OK; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_REJECT_CONFIG) { + wpa_printf(MSG_INFO, "DPP: TESTING - Reject Config Object"); + status = DPP_STATUS_CONFIG_REJECTED; + } +#endif /* CONFIG_TESTING_OPTIONS */ fail: - wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); + if (status != DPP_STATUS_OK) + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); +#ifdef CONFIG_DPP2 + if (auth->peer_version >= 2 && + auth->conf_resp_status == DPP_STATUS_OK) { + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result"); + msg = dpp_build_conf_result(auth, status); + if (!msg) + goto fail2; + + wpa_msg(hapd->msg_ctx, MSG_INFO, + DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", + MAC2STR(addr), auth->curr_freq, + DPP_PA_CONFIGURATION_RESULT); + hostapd_drv_send_action(hapd, auth->curr_freq, 0, + addr, wpabuf_head(msg), + wpabuf_len(msg)); + wpabuf_free(msg); + + /* This exchange will be terminated in the TX status handler */ + auth->connect_on_tx_status = 1; + return; + } +fail2: +#endif /* CONFIG_DPP2 */ dpp_auth_deinit(hapd->dpp_auth); hapd->dpp_auth = NULL; } @@ -1121,7 +755,7 @@ fail: static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd) { struct dpp_authentication *auth = hapd->dpp_auth; - struct wpabuf *buf, *conf_req; + struct wpabuf *buf; char json[100]; int res; int netrole_ap = 1; @@ -1133,34 +767,13 @@ static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd) netrole_ap ? "ap" : "sta"); wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json); - conf_req = dpp_build_conf_req(auth, json); - if (!conf_req) { + buf = dpp_build_conf_req(auth, json); + if (!buf) { wpa_printf(MSG_DEBUG, "DPP: No configuration request data available"); return; } - buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req)); - if (!buf) { - wpabuf_free(conf_req); - return; - } - - /* Advertisement Protocol IE */ - wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); - wpabuf_put_u8(buf, 8); /* Length */ - wpabuf_put_u8(buf, 0x7f); - wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); - wpabuf_put_u8(buf, 5); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, DPP_OUI_TYPE); - wpabuf_put_u8(buf, 0x01); - - /* GAS Query */ - wpabuf_put_le16(buf, wpabuf_len(conf_req)); - wpabuf_put_buf(buf, conf_req); - wpabuf_free(conf_req); - wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (freq %u MHz)", MAC2STR(auth->peer_mac_addr), auth->curr_freq); @@ -1281,6 +894,63 @@ static void hostapd_dpp_rx_auth_conf(struct hostapd_data *hapd, const u8 *src, } +#ifdef CONFIG_DPP2 + +static void hostapd_dpp_config_result_wait_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct dpp_authentication *auth = hapd->dpp_auth; + + if (!auth || !auth->waiting_conf_result) + return; + + wpa_printf(MSG_DEBUG, + "DPP: Timeout while waiting for Configuration Result"); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); + dpp_auth_deinit(auth); + hapd->dpp_auth = NULL; +} + + +static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + enum dpp_status_error status; + + wpa_printf(MSG_DEBUG, "DPP: Configuration Result from " MACSTR, + MAC2STR(src)); + + if (!auth || !auth->waiting_conf_result) { + wpa_printf(MSG_DEBUG, + "DPP: No DPP Configuration waiting for result - drop"); + return; + } + + if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected " + MACSTR ") - drop", MAC2STR(auth->peer_mac_addr)); + return; + } + + status = dpp_conf_result_rx(auth, hdr, buf, len); + + hostapd_drv_send_action_cancel_wait(hapd); + hostapd_dpp_listen_stop(hapd); + if (status == DPP_STATUS_OK) + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT); + else + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); + dpp_auth_deinit(auth); + hapd->dpp_auth = NULL; + eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd, + NULL); +} + +#endif /* CONFIG_DPP2 */ + + static void hostapd_dpp_send_peer_disc_resp(struct hostapd_data *hapd, const u8 *src, unsigned int freq, u8 trans_id, @@ -1596,24 +1266,10 @@ hostapd_dpp_rx_pkex_commit_reveal_req(struct hostapd_data *hapd, const u8 *src, wpabuf_head(msg), wpabuf_len(msg)); wpabuf_free(msg); - bi = os_zalloc(sizeof(*bi)); + bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq); if (!bi) return; - bi->id = hapd_dpp_next_id(hapd); - bi->type = DPP_BOOTSTRAP_PKEX; - os_memcpy(bi->mac_addr, src, ETH_ALEN); - bi->num_freq = 1; - bi->freq[0] = freq; - bi->curve = pkex->own_bi->curve; - bi->pubkey = pkex->peer_bootstrap_key; - pkex->peer_bootstrap_key = NULL; - dpp_pkex_free(pkex); hapd->dpp_pkex = NULL; - if (dpp_bootstrap_key_hash(bi) < 0) { - dpp_bootstrap_info_free(bi); - return; - } - dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list); } @@ -1623,7 +1279,7 @@ hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src, unsigned int freq) { int res; - struct dpp_bootstrap_info *bi, *own_bi; + struct dpp_bootstrap_info *bi; struct dpp_pkex *pkex = hapd->dpp_pkex; char cmd[500]; @@ -1641,26 +1297,10 @@ hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src, return; } - own_bi = pkex->own_bi; - - bi = os_zalloc(sizeof(*bi)); + bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq); if (!bi) return; - bi->id = hapd_dpp_next_id(hapd); - bi->type = DPP_BOOTSTRAP_PKEX; - os_memcpy(bi->mac_addr, src, ETH_ALEN); - bi->num_freq = 1; - bi->freq[0] = freq; - bi->curve = own_bi->curve; - bi->pubkey = pkex->peer_bootstrap_key; - pkex->peer_bootstrap_key = NULL; - dpp_pkex_free(pkex); hapd->dpp_pkex = NULL; - if (dpp_bootstrap_key_hash(bi) < 0) { - dpp_bootstrap_info_free(bi); - return; - } - dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list); os_snprintf(cmd, sizeof(cmd), " peer=%u %s", bi->id, @@ -1744,6 +1384,11 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, hostapd_dpp_rx_pkex_commit_reveal_resp(hapd, src, hdr, buf, len, freq); break; +#ifdef CONFIG_DPP2 + case DPP_PA_CONFIGURATION_RESULT: + hostapd_dpp_rx_conf_result(hapd, src, hdr, buf, len); + break; +#endif /* CONFIG_DPP2 */ default: wpa_printf(MSG_DEBUG, "DPP: Ignored unsupported frame subtype %d", type); @@ -1790,11 +1435,28 @@ hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa, void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok) { - if (!hapd->dpp_auth) + struct dpp_authentication *auth = hapd->dpp_auth; + + if (!auth) return; + wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)", + ok); eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); +#ifdef CONFIG_DPP2 + if (ok && auth->peer_version >= 2 && + auth->conf_resp_status == DPP_STATUS_OK) { + wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result"); + auth->waiting_conf_result = 1; + eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, + hapd, NULL); + eloop_register_timeout(2, 0, + hostapd_dpp_config_result_wait_timeout, + hapd, NULL); + return; + } +#endif /* CONFIG_DPP2 */ hostapd_drv_send_action_cancel_wait(hapd); if (ok) @@ -1806,93 +1468,6 @@ void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok) } -static unsigned int hostapd_dpp_next_configurator_id(struct hostapd_data *hapd) -{ - struct dpp_configurator *conf; - unsigned int max_id = 0; - - dl_list_for_each(conf, &hapd->iface->interfaces->dpp_configurator, - struct dpp_configurator, list) { - if (conf->id > max_id) - max_id = conf->id; - } - return max_id + 1; -} - - -int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd) -{ - char *curve = NULL; - char *key = NULL; - u8 *privkey = NULL; - size_t privkey_len = 0; - int ret = -1; - struct dpp_configurator *conf = NULL; - - curve = get_param(cmd, " curve="); - key = get_param(cmd, " key="); - - if (key) { - privkey_len = os_strlen(key) / 2; - privkey = os_malloc(privkey_len); - if (!privkey || - hexstr2bin(key, privkey, privkey_len) < 0) - goto fail; - } - - conf = dpp_keygen_configurator(curve, privkey, privkey_len); - if (!conf) - goto fail; - - conf->id = hostapd_dpp_next_configurator_id(hapd); - dl_list_add(&hapd->iface->interfaces->dpp_configurator, &conf->list); - ret = conf->id; - conf = NULL; -fail: - os_free(curve); - str_clear_free(key); - bin_clear_free(privkey, privkey_len); - dpp_configurator_free(conf); - return ret; -} - - -static int dpp_configurator_del(struct hapd_interfaces *ifaces, unsigned int id) -{ - struct dpp_configurator *conf, *tmp; - int found = 0; - - dl_list_for_each_safe(conf, tmp, &ifaces->dpp_configurator, - struct dpp_configurator, list) { - if (id && conf->id != id) - continue; - found = 1; - dl_list_del(&conf->list); - dpp_configurator_free(conf); - } - - if (id == 0) - return 0; /* flush succeeds regardless of entries found */ - return found ? 0 : -1; -} - - -int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id) -{ - unsigned int id_val; - - if (os_strcmp(id, "*") == 0) { - id_val = 0; - } else { - id_val = atoi(id); - if (id_val == 0) - return -1; - } - - return dpp_configurator_del(hapd->iface->interfaces, id_val); -} - - int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd) { struct dpp_authentication *auth; @@ -1905,7 +1480,8 @@ int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd) curve = get_param(cmd, " curve="); hostapd_dpp_set_testing_options(hapd, auth); - if (hostapd_dpp_set_configurator(hapd, auth, cmd) == 0 && + if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx, + auth, cmd) == 0 && dpp_configurator_own_config(auth, curve, 1) == 0) { hostapd_dpp_handle_config_obj(hapd, auth); ret = 0; @@ -1918,19 +1494,6 @@ int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd) } -int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id, - char *buf, size_t buflen) -{ - struct dpp_configurator *conf; - - conf = hostapd_dpp_configurator_get_id(hapd, id); - if (!conf) - return -1; - - return dpp_configurator_get_key(conf, buf, buflen); -} - - int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd) { struct dpp_bootstrap_info *own_bi; @@ -1940,7 +1503,7 @@ int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd) if (!pos) return -1; pos += 5; - own_bi = dpp_bootstrap_get_id(hapd, atoi(pos)); + own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos)); if (!own_bi) { wpa_printf(MSG_DEBUG, "DPP: Identified bootstrap info not found"); @@ -2070,6 +1633,10 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd) eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); +#ifdef CONFIG_DPP2 + eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd, + NULL); +#endif /* CONFIG_DPP2 */ dpp_auth_deinit(hapd->dpp_auth); hapd->dpp_auth = NULL; hostapd_dpp_pkex_remove(hapd, "*"); @@ -2077,20 +1644,3 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd) os_free(hapd->dpp_configurator_params); hapd->dpp_configurator_params = NULL; } - - -void hostapd_dpp_init_global(struct hapd_interfaces *ifaces) -{ - dl_list_init(&ifaces->dpp_bootstrap); - dl_list_init(&ifaces->dpp_configurator); - ifaces->dpp_init_done = 1; -} - - -void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces) -{ - if (!ifaces->dpp_init_done) - return; - dpp_bootstrap_del(ifaces, 0); - dpp_configurator_del(ifaces, 0); -} diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h index 3ef7c14567e88..449ca16d118ff 100644 --- a/src/ap/dpp_hostapd.h +++ b/src/ap/dpp_hostapd.h @@ -10,12 +10,6 @@ #define DPP_HOSTAPD_H int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd); -int hostapd_dpp_bootstrap_gen(struct hostapd_data *hapd, const char *cmd); -int hostapd_dpp_bootstrap_remove(struct hostapd_data *hapd, const char *id); -const char * hostapd_dpp_bootstrap_get_uri(struct hostapd_data *hapd, - unsigned int id); -int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id, - char *reply, int reply_size); int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd); int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd); void hostapd_dpp_listen_stop(struct hostapd_data *hapd); diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index a726a6ff87e01..8ddf754f60a74 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -15,6 +15,7 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" +#include "common/dpp.h" #include "crypto/random.h" #include "p2p/p2p.h" #include "wps/wps.h" @@ -38,6 +39,7 @@ #include "mbo_ap.h" #include "dpp_hostapd.h" #include "fils_hlp.h" +#include "neighbor_db.h" #ifdef CONFIG_FILS @@ -304,6 +306,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, return -1; } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + hapd->iface->freq, ie, ielen, elems.mdie, elems.mdie_len, elems.owe_dh, elems.owe_dh_len); @@ -563,6 +566,38 @@ skip_wpa_check: } #endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP2 + dpp_pfs_free(sta->dpp_pfs); + sta->dpp_pfs = NULL; + + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && + hapd->conf->dpp_netaccesskey && sta->wpa_sm && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP && + elems.owe_dh) { + sta->dpp_pfs = dpp_pfs_init( + wpabuf_head(hapd->conf->dpp_netaccesskey), + wpabuf_len(hapd->conf->dpp_netaccesskey)); + if (!sta->dpp_pfs) { + wpa_printf(MSG_DEBUG, + "DPP: Could not initialize PFS"); + /* Try to continue without PFS */ + goto pfs_fail; + } + + if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh, + elems.owe_dh_len) < 0) { + dpp_pfs_free(sta->dpp_pfs); + sta->dpp_pfs = NULL; + reason = WLAN_REASON_UNSPECIFIED; + goto fail; + } + } + + wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ? + sta->dpp_pfs->secret : NULL); + pfs_fail: +#endif /* CONFIG_DPP2 */ + #if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS) || defined(CONFIG_OWE) hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); @@ -739,9 +774,12 @@ void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr, void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, int offset, int width, int cf1, int cf2) { + /* TODO: If OCV is enabled deauth STAs that don't perform a SA Query */ + #ifdef NEED_AP_MLME int channel, chwidth, is_dfs; u8 seg0_idx = 0, seg1_idx = 0; + size_t i; hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, @@ -824,6 +862,9 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED "freq=%d dfs=%d", freq, is_dfs); } + + for (i = 0; i < hapd->iface->num_bss; i++) + hostapd_neighbor_set_own_report(hapd->iface->bss[i]); #endif /* NEED_AP_MLME */ } @@ -1065,6 +1106,7 @@ fail: } +#ifndef NEED_AP_MLME static void hostapd_action_rx(struct hostapd_data *hapd, struct rx_mgmt *drv_mgmt) { @@ -1077,7 +1119,7 @@ static void hostapd_action_rx(struct hostapd_data *hapd, if (drv_mgmt->frame_len < IEEE80211_HDRLEN + 2 + 1) return; - plen = drv_mgmt->frame_len - IEEE80211_HDRLEN - 1; + plen = drv_mgmt->frame_len - IEEE80211_HDRLEN; mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame; fc = le_to_host16(mgmt->frame_control); @@ -1097,22 +1139,20 @@ static void hostapd_action_rx(struct hostapd_data *hapd, } #ifdef CONFIG_IEEE80211R_AP if (mgmt->u.action.category == WLAN_ACTION_FT) { - const u8 *payload = drv_mgmt->frame + 24 + 1; - - wpa_ft_action_rx(sta->wpa_sm, payload, plen); + wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, plen); + return; } #endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W - if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY && plen >= 4) { - ieee802_11_sa_query_action( - hapd, mgmt->sa, - mgmt->u.action.u.sa_query_resp.action, - mgmt->u.action.u.sa_query_resp.trans_id); + if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY) { + ieee802_11_sa_query_action(hapd, mgmt, drv_mgmt->frame_len); + return; } #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WNM_AP if (mgmt->u.action.category == WLAN_ACTION_WNM) { ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len); + return; } #endif /* CONFIG_WNM_AP */ #ifdef CONFIG_FST @@ -1122,7 +1162,7 @@ static void hostapd_action_rx(struct hostapd_data *hapd, } #endif /* CONFIG_FST */ #ifdef CONFIG_DPP - if (plen >= 1 + 4 && + if (plen >= 2 + 4 && mgmt->u.action.u.vs_public_action.action == WLAN_PA_VENDOR_SPECIFIC && WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) == @@ -1139,6 +1179,7 @@ static void hostapd_action_rx(struct hostapd_data *hapd, } #endif /* CONFIG_DPP */ } +#endif /* NEED_AP_MLME */ #ifdef NEED_AP_MLME @@ -1600,10 +1641,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, if (!data->rx_mgmt.frame) break; #ifdef NEED_AP_MLME - if (hostapd_mgmt_rx(hapd, &data->rx_mgmt) > 0) - break; -#endif /* NEED_AP_MLME */ + hostapd_mgmt_rx(hapd, &data->rx_mgmt); +#else /* NEED_AP_MLME */ hostapd_action_rx(hapd, &data->rx_mgmt); +#endif /* NEED_AP_MLME */ break; case EVENT_RX_PROBE_REQ: if (data->rx_probe_req.sa == NULL || @@ -1725,6 +1766,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, hostapd_reconfig_encryption(hapd); hapd->reenable_beacon = 1; ieee802_11_set_beacon(hapd); +#ifdef NEED_AP_MLME + } else if (hapd->disabled && hapd->iface->cac_started) { + wpa_printf(MSG_DEBUG, "DFS: restarting pending CAC"); + hostapd_handle_dfs(hapd->iface); +#endif /* NEED_AP_MLME */ } break; case EVENT_INTERFACE_DISABLED: diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c index 296d5c2ddf311..a510ee3e29fdc 100644 --- a/src/ap/eap_user_db.c +++ b/src/ap/eap_user_db.c @@ -139,6 +139,7 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, struct hostapd_eap_user *user = NULL; char id_str[256], cmd[300]; size_t i; + int res; if (identity_len >= sizeof(id_str)) { wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d", @@ -174,6 +175,7 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, if (hapd->tmp_eap_user.identity == NULL) return NULL; os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len); + hapd->tmp_eap_user.identity_len = identity_len; if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) { wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s", @@ -182,9 +184,12 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, return NULL; } - os_snprintf(cmd, sizeof(cmd), - "SELECT * FROM users WHERE identity='%s' AND phase2=%d;", - id_str, phase2); + res = os_snprintf(cmd, sizeof(cmd), + "SELECT * FROM users WHERE identity='%s' AND phase2=%d;", + id_str, phase2); + if (os_snprintf_error(sizeof(cmd), res)) + goto fail; + wpa_printf(MSG_DEBUG, "DB: %s", cmd); if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) != SQLITE_OK) { @@ -214,6 +219,7 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, } } +fail: sqlite3_close(db); return user; diff --git a/src/ap/fils_hlp.c b/src/ap/fils_hlp.c index 2a359ab03c818..6da514a4d0fb4 100644 --- a/src/ap/fils_hlp.c +++ b/src/ap/fils_hlp.c @@ -580,6 +580,19 @@ int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta, u8 *tmp, *tmp_pos; int ret = 0; + if (sta->fils_pending_assoc_req && + eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta)) { + /* Do not process FILS HLP request again if the station + * retransmits (Re)Association Request frame before the previous + * HLP response has either been received or timed out. */ + wpa_printf(MSG_DEBUG, + "FILS: Do not relay another HLP request from " + MACSTR + " before processing of the already pending one has been completed", + MAC2STR(sta->addr)); + return 1; + } + /* Old DHCPDISCOVER is not needed anymore, if it was still pending */ wpabuf_free(sta->hlp_dhcp_discover); sta->hlp_dhcp_discover = NULL; diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 7501bac6e42a1..20c8e8f5a4f71 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -1,6 +1,6 @@ /* * hostapd / Initialization and configuration - * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -348,10 +348,11 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) if (!hapd->started) { wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started", - __func__, hapd->conf->iface); + __func__, hapd->conf ? hapd->conf->iface : "N/A"); return; } hapd->started = 0; + hapd->beacon_set_done = 0; wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface); iapp_deinit(hapd->iapp); @@ -417,6 +418,20 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) hostapd_clean_rrm(hapd); fils_hlp_deinit(hapd); + +#ifdef CONFIG_SAE + { + struct hostapd_sae_commit_queue *q; + + while ((q = dl_list_first(&hapd->sae_commit_queue, + struct hostapd_sae_commit_queue, + list))) { + dl_list_del(&q->list); + os_free(q); + } + } + eloop_cancel_timeout(auth_sae_process_commit, hapd, NULL); +#endif /* CONFIG_SAE */ } @@ -431,7 +446,7 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) static void hostapd_cleanup(struct hostapd_data *hapd) { wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd, - hapd->conf->iface); + hapd->conf ? hapd->conf->iface : "N/A"); if (hapd->iface->interfaces && hapd->iface->interfaces->ctrl_iface_deinit) { wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_TERMINATING); @@ -506,7 +521,7 @@ static void hostapd_cleanup_iface(struct hostapd_iface *iface) static void hostapd_clear_wep(struct hostapd_data *hapd) { - if (hapd->drv_priv && !hapd->iface->driver_ap_teardown) { + if (hapd->drv_priv && !hapd->iface->driver_ap_teardown && hapd->conf) { hostapd_set_privacy(hapd, 0); hostapd_broadcast_wep_clear(hapd); } @@ -658,8 +673,10 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) for (i = 5; i > 5 - j; i--) mask[i] = 0; j = bits % 8; - while (j--) + while (j) { + j--; mask[i] <<= 1; + } skip_mask_ext: wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)", @@ -1668,127 +1685,6 @@ void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd, #endif /* CONFIG_FST */ - -#ifdef NEED_AP_MLME -static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd, - int ht, int vht) -{ - if (!ht && !vht) - return NR_CHAN_WIDTH_20; - if (!hapd->iconf->secondary_channel) - return NR_CHAN_WIDTH_20; - if (!vht || hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT) - return NR_CHAN_WIDTH_40; - if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ) - return NR_CHAN_WIDTH_80; - if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_160MHZ) - return NR_CHAN_WIDTH_160; - if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) - return NR_CHAN_WIDTH_80P80; - return NR_CHAN_WIDTH_20; -} -#endif /* NEED_AP_MLME */ - - -static void hostapd_set_own_neighbor_report(struct hostapd_data *hapd) -{ -#ifdef NEED_AP_MLME - u16 capab = hostapd_own_capab_info(hapd); - int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n; - int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac; - struct wpa_ssid_value ssid; - u8 channel, op_class; - u8 center_freq1_idx = 0, center_freq2_idx = 0; - enum nr_chan_width width; - u32 bssid_info; - struct wpabuf *nr; - - if (!(hapd->conf->radio_measurements[0] & - WLAN_RRM_CAPS_NEIGHBOR_REPORT)) - return; - - bssid_info = 3; /* AP is reachable */ - bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */ - bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */ - - if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) - bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT; - - bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */ - - if (hapd->conf->wmm_enabled) { - bssid_info |= NEI_REP_BSSID_INFO_QOS; - - if (hapd->conf->wmm_uapsd && - (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD)) - bssid_info |= NEI_REP_BSSID_INFO_APSD; - } - - if (ht) { - bssid_info |= NEI_REP_BSSID_INFO_HT | - NEI_REP_BSSID_INFO_DELAYED_BA; - - /* VHT bit added in IEEE P802.11-REVmc/D4.3 */ - if (vht) - bssid_info |= NEI_REP_BSSID_INFO_VHT; - } - - /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */ - - if (ieee80211_freq_to_channel_ext(hapd->iface->freq, - hapd->iconf->secondary_channel, - hapd->iconf->vht_oper_chwidth, - &op_class, &channel) == - NUM_HOSTAPD_MODES) - return; - width = hostapd_get_nr_chan_width(hapd, ht, vht); - if (vht) { - center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx; - if (width == NR_CHAN_WIDTH_80P80) - center_freq2_idx = - hapd->iconf->vht_oper_centr_freq_seg1_idx; - } else if (ht) { - ieee80211_freq_to_chan(hapd->iface->freq + - 10 * hapd->iconf->secondary_channel, - ¢er_freq1_idx); - } - - ssid.ssid_len = hapd->conf->ssid.ssid_len; - os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len); - - /* - * Neighbor Report element size = BSSID + BSSID info + op_class + chan + - * phy type + wide bandwidth channel subelement. - */ - nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5); - if (!nr) - return; - - wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN); - wpabuf_put_le32(nr, bssid_info); - wpabuf_put_u8(nr, op_class); - wpabuf_put_u8(nr, channel); - wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht)); - - /* - * Wide Bandwidth Channel subelement may be needed to allow the - * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0 - * Figure 9-301. - */ - wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN); - wpabuf_put_u8(nr, 3); - wpabuf_put_u8(nr, width); - wpabuf_put_u8(nr, center_freq1_idx); - wpabuf_put_u8(nr, center_freq2_idx); - - hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci, - hapd->iconf->civic, hapd->iconf->stationary_ap); - - wpabuf_free(nr); -#endif /* NEED_AP_MLME */ -} - - #ifdef CONFIG_OWE static int hostapd_owe_iface_iter(struct hostapd_iface *iface, void *ctx) @@ -1988,15 +1884,17 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, } } - if (hapd->iconf->rts_threshold > -1 && - hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) { + if (hapd->iconf->rts_threshold >= -1 && + hostapd_set_rts(hapd, hapd->iconf->rts_threshold) && + hapd->iconf->rts_threshold >= -1) { wpa_printf(MSG_ERROR, "Could not set RTS threshold for " "kernel driver"); goto fail; } - if (hapd->iconf->fragm_threshold > -1 && - hostapd_set_frag(hapd, hapd->iconf->fragm_threshold)) { + if (hapd->iconf->fragm_threshold >= -1 && + hostapd_set_frag(hapd, hapd->iconf->fragm_threshold) && + hapd->iconf->fragm_threshold != -1) { wpa_printf(MSG_ERROR, "Could not set fragmentation threshold " "for kernel driver"); goto fail; @@ -2009,11 +1907,14 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, if (j) os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN); if (hostapd_setup_bss(hapd, j == 0)) { - do { + for (;;) { hapd = iface->bss[j]; hostapd_bss_deinit_no_free(hapd); hostapd_free_hapd_data(hapd); - } while (j-- > 0); + if (j == 0) + break; + j--; + } goto fail; } if (is_zero_ether_addr(hapd->conf->bssid)) @@ -2085,7 +1986,7 @@ dfs_offload: iface->interfaces->terminate_on_error--; for (j = 0; j < iface->num_bss; j++) - hostapd_set_own_neighbor_report(iface->bss[j]); + hostapd_neighbor_set_own_report(iface->bss[j]); return 0; @@ -2266,6 +2167,9 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, dl_list_init(&hapd->l2_queue); dl_list_init(&hapd->l2_oui_queue); #endif /* CONFIG_IEEE80211R_AP */ +#ifdef CONFIG_SAE + dl_list_init(&hapd->sae_commit_queue); +#endif /* CONFIG_SAE */ return hapd; } @@ -2276,7 +2180,7 @@ static void hostapd_bss_deinit(struct hostapd_data *hapd) if (!hapd) return; wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__, - hapd->conf->iface); + hapd->conf ? hapd->conf->iface : "N/A"); hostapd_bss_deinit_no_free(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); hostapd_cleanup(hapd); @@ -2303,7 +2207,7 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) } #endif /* CONFIG_FST */ - for (j = iface->num_bss - 1; j >= 0; j--) { + for (j = (int) iface->num_bss - 1; j >= 0; j--) { if (!iface->bss) break; hostapd_bss_deinit(iface->bss[j]); diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index d304c1171810c..790d377548702 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -66,9 +66,7 @@ struct hapd_interfaces { int eloop_initialized; #ifdef CONFIG_DPP - int dpp_init_done; - struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */ - struct dl_list dpp_configurator; /* struct dpp_configurator */ + struct dpp_global *dpp; #endif /* CONFIG_DPP */ }; @@ -129,6 +127,13 @@ struct hostapd_neighbor_entry { int stationary; }; +struct hostapd_sae_commit_queue { + struct dl_list list; + int rssi; + size_t len; + u8 msg[]; +}; + /** * struct hostapd_data - hostapd per-BSS data structure */ @@ -307,7 +312,10 @@ struct hostapd_data { /** Key used for generating SAE anti-clogging tokens */ u8 sae_token_key[8]; struct os_reltime last_sae_token_key_update; + u16 sae_token_idx; + u16 sae_pending_token_idx[256]; int dot11RSNASAERetransPeriod; /* msec */ + struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */ #endif /* CONFIG_SAE */ #ifdef CONFIG_TESTING_OPTIONS diff --git a/src/ap/hs20.c b/src/ap/hs20.c index e265569aef389..532580e7c66c4 100644 --- a/src/ap/hs20.c +++ b/src/ap/hs20.c @@ -25,17 +25,20 @@ u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid) if (!hapd->conf->hs20) return eid; *eid++ = WLAN_EID_VENDOR_SPECIFIC; - *eid++ = 7; + *eid++ = hapd->conf->hs20_release < 2 ? 5 : 7; WPA_PUT_BE24(eid, OUI_WFA); eid += 3; *eid++ = HS20_INDICATION_OUI_TYPE; - conf = HS20_VERSION; /* Release Number */ - conf |= HS20_ANQP_DOMAIN_ID_PRESENT; + conf = (hapd->conf->hs20_release - 1) << 4; /* Release Number */ + if (hapd->conf->hs20_release >= 2) + conf |= HS20_ANQP_DOMAIN_ID_PRESENT; if (hapd->conf->disable_dgaf) conf |= HS20_DGAF_DISABLED; *eid++ = conf; - WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id); - eid += 2; + if (hapd->conf->hs20_release >= 2) { + WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id); + eid += 2; + } return eid; } @@ -84,6 +87,10 @@ u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid) capab |= WPA_CAPABILITY_MFPR; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + if (hapd->conf->ocv) + capab |= WPA_CAPABILITY_OCVC; +#endif /* CONFIG_OCV */ WPA_PUT_LE16(eid, capab); eid += 2; diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 5279abca1f1fa..9d3d990a281fd 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -229,9 +229,6 @@ static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface) { int pri_chan, sec_chan; - if (!iface->conf->secondary_channel) - return 1; /* HT40 not used */ - pri_chan = iface->conf->channel; sec_chan = pri_chan + iface->conf->secondary_channel * 4; @@ -697,30 +694,25 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface) static int hostapd_is_usable_chan(struct hostapd_iface *iface, int channel, int primary) { - 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) - continue; - - if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) - return 1; + chan = hw_get_channel_chan(iface->current_mode, channel, NULL); + if (!chan) + return 0; - wpa_printf(MSG_DEBUG, - "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s", - primary ? "" : "Configured HT40 secondary ", - i, chan->chan, chan->flag, - chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "", - chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); - } + if ((primary && chan_pri_allowed(chan)) || + (!primary && !(chan->flag & HOSTAPD_CHAN_DISABLED))) + return 1; - wpa_printf(MSG_INFO, "Channel %d (%s) not allowed for AP mode", - channel, primary ? "primary" : "secondary"); + wpa_printf(MSG_INFO, + "Channel %d (%s) not allowed for AP mode, flags: 0x%x%s%s", + channel, primary ? "primary" : "secondary", + chan->flag, + chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "", + chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); return 0; } @@ -728,6 +720,12 @@ static int hostapd_is_usable_chan(struct hostapd_iface *iface, static int hostapd_is_usable_chans(struct hostapd_iface *iface) { int secondary_chan; + struct hostapd_channel_data *pri_chan; + + pri_chan = hw_get_channel_chan(iface->current_mode, + iface->conf->channel, NULL); + if (!pri_chan) + return 0; if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1)) return 0; @@ -742,13 +740,15 @@ static int hostapd_is_usable_chans(struct hostapd_iface *iface) /* Both HT40+ and HT40- are set, pick a valid secondary channel */ secondary_chan = iface->conf->channel + 4; - if (hostapd_is_usable_chan(iface, secondary_chan, 0)) { + if (hostapd_is_usable_chan(iface, secondary_chan, 0) && + (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) { iface->conf->secondary_channel = 1; return 1; } secondary_chan = iface->conf->channel - 4; - if (hostapd_is_usable_chan(iface, secondary_chan, 0)) { + if (hostapd_is_usable_chan(iface, secondary_chan, 0) && + (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) { iface->conf->secondary_channel = -1; return 1; } diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index f9bb99d985495..fde19b526f051 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -21,6 +21,8 @@ #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "common/sae.h" +#include "common/dpp.h" +#include "common/ocv.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" @@ -61,6 +63,25 @@ prepare_auth_resp_fils(struct hostapd_data *hapd, const u8 *msk, size_t msk_len, int *is_pub); #endif /* CONFIG_FILS */ +static void handle_auth(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len, + int rssi, int from_queue); + + +u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid) +{ + u8 multi_ap_val = 0; + + if (!hapd->conf->multi_ap) + return eid; + if (hapd->conf->multi_ap & BACKHAUL_BSS) + multi_ap_val |= MULTI_AP_BACKHAUL_BSS; + if (hapd->conf->multi_ap & FRONTHAUL_BSS) + multi_ap_val |= MULTI_AP_FRONTHAUL_BSS; + + return eid + add_multi_ap_ie(eid, 9, multi_ap_val); +} + u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) { @@ -403,6 +424,15 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, return NULL; } + if (pw && pw->vlan_id) { + if (!sta->sae->tmp) { + wpa_printf(MSG_INFO, + "SAE: No temporary data allocated - cannot store VLAN ID"); + return NULL; + } + sta->sae->tmp->vlan_id = pw->vlan_id; + } + buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN + (rx_id ? 3 + os_strlen(rx_id) : 0)); if (buf == NULL) @@ -492,22 +522,58 @@ static int use_sae_anti_clogging(struct hostapd_data *hapd) return 1; } + /* In addition to already existing open SAE sessions, check whether + * there are enough pending commit messages in the processing queue to + * potentially result in too many open sessions. */ + if (open + dl_list_len(&hapd->sae_commit_queue) >= + hapd->conf->sae_anti_clogging_threshold) + return 1; + return 0; } +static u8 sae_token_hash(struct hostapd_data *hapd, const u8 *addr) +{ + u8 hash[SHA256_MAC_LEN]; + + hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), + addr, ETH_ALEN, hash); + return hash[0]; +} + + static int check_sae_token(struct hostapd_data *hapd, const u8 *addr, const u8 *token, size_t token_len) { u8 mac[SHA256_MAC_LEN]; + const u8 *addrs[2]; + size_t len[2]; + u16 token_idx; + u8 idx; if (token_len != SHA256_MAC_LEN) return -1; - if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), - addr, ETH_ALEN, mac) < 0 || - os_memcmp_const(token, mac, SHA256_MAC_LEN) != 0) + idx = sae_token_hash(hapd, addr); + token_idx = hapd->sae_pending_token_idx[idx]; + if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) { + wpa_printf(MSG_DEBUG, "SAE: Invalid anti-clogging token from " + MACSTR " - token_idx 0x%04x, expected 0x%04x", + MAC2STR(addr), WPA_GET_BE16(token), token_idx); + return -1; + } + + addrs[0] = addr; + len[0] = ETH_ALEN; + addrs[1] = token; + len[1] = 2; + if (hmac_sha256_vector(hapd->sae_token_key, sizeof(hapd->sae_token_key), + 2, addrs, len, mac) < 0 || + os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0) return -1; + hapd->sae_pending_token_idx[idx] = 0; /* invalidate used token */ + return 0; } @@ -518,16 +584,25 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, struct wpabuf *buf; u8 *token; struct os_reltime now; + u8 idx[2]; + const u8 *addrs[2]; + size_t len[2]; + u8 p_idx; + u16 token_idx; os_get_reltime(&now); if (!os_reltime_initialized(&hapd->last_sae_token_key_update) || - os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60)) { + os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60) || + hapd->sae_token_idx == 0xffff) { if (random_get_bytes(hapd->sae_token_key, sizeof(hapd->sae_token_key)) < 0) return NULL; wpa_hexdump(MSG_DEBUG, "SAE: Updated token key", hapd->sae_token_key, sizeof(hapd->sae_token_key)); hapd->last_sae_token_key_update = now; + hapd->sae_token_idx = 0; + os_memset(hapd->sae_pending_token_idx, 0, + sizeof(hapd->sae_pending_token_idx)); } buf = wpabuf_alloc(sizeof(le16) + SHA256_MAC_LEN); @@ -536,9 +611,25 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, wpabuf_put_le16(buf, group); /* Finite Cyclic Group */ + p_idx = sae_token_hash(hapd, addr); + token_idx = hapd->sae_pending_token_idx[p_idx]; + if (!token_idx) { + hapd->sae_token_idx++; + token_idx = hapd->sae_token_idx; + hapd->sae_pending_token_idx[p_idx] = token_idx; + } + WPA_PUT_BE16(idx, token_idx); token = wpabuf_put(buf, SHA256_MAC_LEN); - hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), - addr, ETH_ALEN, token); + addrs[0] = addr; + len[0] = ETH_ALEN; + addrs[1] = idx; + len[1] = sizeof(idx); + if (hmac_sha256_vector(hapd->sae_token_key, sizeof(hapd->sae_token_key), + 2, addrs, len, token) < 0) { + wpabuf_free(buf); + return NULL; + } + WPA_PUT_BE16(token, token_idx); return buf; } @@ -610,8 +701,52 @@ static void sae_set_retransmit_timer(struct hostapd_data *hapd, } +static void sae_sme_send_external_auth_status(struct hostapd_data *hapd, + struct sta_info *sta, u16 status) +{ + struct external_auth params; + + os_memset(¶ms, 0, sizeof(params)); + params.status = status; + params.bssid = sta->addr; + if (status == WLAN_STATUS_SUCCESS && sta->sae) + params.pmkid = sta->sae->pmkid; + + hostapd_drv_send_external_auth_status(hapd, ¶ms); +} + + void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) { +#ifndef CONFIG_NO_VLAN + struct vlan_description vlan_desc; + + if (sta->sae->tmp && sta->sae->tmp->vlan_id > 0) { + wpa_printf(MSG_DEBUG, "SAE: Assign STA " MACSTR + " to VLAN ID %d", + MAC2STR(sta->addr), sta->sae->tmp->vlan_id); + + os_memset(&vlan_desc, 0, sizeof(vlan_desc)); + vlan_desc.notempty = 1; + vlan_desc.untagged = sta->sae->tmp->vlan_id; + if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) { + wpa_printf(MSG_INFO, + "Invalid VLAN ID %d in sae_password", + sta->sae->tmp->vlan_id); + return; + } + + if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 || + ap_sta_bind_vlan(hapd, sta) < 0) { + wpa_printf(MSG_INFO, + "Failed to assign VLAN ID %d from sae_password to " + MACSTR, sta->sae->tmp->vlan_id, + MAC2STR(sta->addr)); + return; + } + } +#endif /* CONFIG_NO_VLAN */ + sta->flags |= WLAN_STA_AUTH; sta->auth_alg = WLAN_AUTH_SAE; mlme_authenticate_indication(hapd, sta); @@ -619,14 +754,18 @@ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm"); wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, sta->sae->pmk, sta->sae->pmkid); + sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS); } static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *bssid, u8 auth_transaction) + const u8 *bssid, u8 auth_transaction, int allow_reuse, + int *sta_removed) { int ret; + *sta_removed = 0; + if (auth_transaction != 1 && auth_transaction != 2) return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -636,7 +775,8 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, switch (sta->sae->state) { case SAE_NOTHING: if (auth_transaction == 1) { - ret = auth_sae_send_commit(hapd, sta, bssid, 1); + ret = auth_sae_send_commit(hapd, sta, bssid, + !allow_reuse); if (ret) return ret; sae_set_state(sta, SAE_COMMITTED, "Sent Commit"); @@ -725,7 +865,8 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, * step to get to Accepted without waiting for * additional events. */ - return sae_sm_step(hapd, sta, bssid, auth_transaction); + return sae_sm_step(hapd, sta, bssid, auth_transaction, + 0, sta_removed); } break; case SAE_CONFIRMED: @@ -758,8 +899,9 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR ") doing reauthentication", MAC2STR(sta->addr)); - ap_free_sta(hapd, sta); wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); + ap_free_sta(hapd, sta); + *sta_removed = 1; } else if (auth_transaction == 1) { wpa_printf(MSG_DEBUG, "SAE: Start reauthentication"); ret = auth_sae_send_commit(hapd, sta, bssid, 1); @@ -795,18 +937,21 @@ static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta) { struct sae_data *sae = sta->sae; int i, *groups = hapd->conf->sae_groups; + int default_groups[] = { 19, 0 }; if (sae->state != SAE_COMMITTED) return; wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group); - for (i = 0; groups && groups[i] > 0; i++) { + if (!groups) + groups = default_groups; + for (i = 0; groups[i] > 0; i++) { if (sae->group == groups[i]) break; } - if (!groups || groups[i] <= 0) { + if (groups[i] <= 0) { wpa_printf(MSG_DEBUG, "SAE: Previously selected group not found from the current configuration"); return; @@ -835,11 +980,16 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, { int resp = WLAN_STATUS_SUCCESS; struct wpabuf *data = NULL; + int *groups = hapd->conf->sae_groups; + int default_groups[] = { 19, 0 }; + const u8 *pos, *end; + int sta_removed = 0; + + if (!groups) + groups = default_groups; #ifdef CONFIG_TESTING_OPTIONS if (hapd->conf->sae_reflection_attack && auth_transaction == 1) { - const u8 *pos, *end; - wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack"); pos = mgmt->u.auth.variable; end = ((const u8 *) mgmt) + len; @@ -882,8 +1032,10 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, } if (auth_transaction == 1) { - const u8 *token = NULL, *pos, *end; + const u8 *token = NULL; size_t token_len = 0; + int allow_reuse = 0; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "start SAE authentication (RX commit, status=%u)", @@ -900,8 +1052,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto reply; } - resp = sae_group_allowed(sta->sae, - hapd->conf->sae_groups, + resp = sae_group_allowed(sta->sae, groups, WPA_GET_LE16(pos)); if (resp != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_ERROR, @@ -962,15 +1113,28 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, * to use a different group and that would not be * allowed if we remain in Committed state with the * previously set parameters. */ - sae_set_state(sta, SAE_NOTHING, - "Clear existing state to allow restart"); - sae_clear_data(sta->sae); + pos = mgmt->u.auth.variable; + end = ((const u8 *) mgmt) + len; + if (end - pos >= (int) sizeof(le16) && + sae_group_allowed(sta->sae, groups, + WPA_GET_LE16(pos)) == + WLAN_STATUS_SUCCESS) { + /* Do not waste resources deriving the same PWE + * again since the same group is reused. */ + sae_set_state(sta, SAE_NOTHING, + "Allow previous PWE to be reused"); + allow_reuse = 1; + } else { + sae_set_state(sta, SAE_NOTHING, + "Clear existing state to allow restart"); + sae_clear_data(sta->sae); + } } resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable, ((const u8 *) mgmt) + len - mgmt->u.auth.variable, &token, - &token_len, hapd->conf->sae_groups); + &token_len, groups); if (resp == SAE_SILENTLY_DISCARD) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message from " MACSTR " due to reflection attack", @@ -1000,7 +1164,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, if (resp != WLAN_STATUS_SUCCESS) goto reply; - if (!token && use_sae_anti_clogging(hapd)) { + if (!token && use_sae_anti_clogging(hapd) && !allow_reuse) { wpa_printf(MSG_DEBUG, "SAE: Request anti-clogging token from " MACSTR, MAC2STR(sta->addr)); @@ -1013,7 +1177,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, goto reply; } - resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction); + resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction, + allow_reuse, &sta_removed); } else if (auth_transaction == 2) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -1054,7 +1219,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, } sta->sae->rc = peer_send_confirm; } - resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction); + resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction, 0, + &sta_removed); } else { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -1066,7 +1232,17 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, } reply: - if (resp != WLAN_STATUS_SUCCESS) { + if (!sta_removed && resp != WLAN_STATUS_SUCCESS) { + pos = mgmt->u.auth.variable; + end = ((const u8 *) mgmt) + len; + + /* Copy the Finite Cyclic Group field from the request if we + * rejected it as unsupported group. */ + if (resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && + !data && end - pos >= 2) + data = wpabuf_alloc_copy(pos, 2); + + sae_sme_send_external_auth_status(hapd, sta, resp); send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, auth_transaction, resp, data ? wpabuf_head(data) : (u8 *) "", @@ -1074,8 +1250,9 @@ reply: } remove_sta: - if (sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS || - status_code != WLAN_STATUS_SUCCESS)) { + if (!sta_removed && sta->added_unassoc && + (resp != WLAN_STATUS_SUCCESS || + status_code != WLAN_STATUS_SUCCESS)) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } @@ -1114,6 +1291,105 @@ int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta) return 0; } + +void auth_sae_process_commit(void *eloop_ctx, void *user_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct hostapd_sae_commit_queue *q; + unsigned int queue_len; + + q = dl_list_first(&hapd->sae_commit_queue, + struct hostapd_sae_commit_queue, list); + if (!q) + return; + wpa_printf(MSG_DEBUG, + "SAE: Process next available message from queue"); + dl_list_del(&q->list); + handle_auth(hapd, (const struct ieee80211_mgmt *) q->msg, q->len, + q->rssi, 1); + os_free(q); + + if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL)) + return; + queue_len = dl_list_len(&hapd->sae_commit_queue); + eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit, + hapd, NULL); +} + + +static void auth_sae_queue(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len, + int rssi) +{ + struct hostapd_sae_commit_queue *q, *q2; + unsigned int queue_len; + const struct ieee80211_mgmt *mgmt2; + + queue_len = dl_list_len(&hapd->sae_commit_queue); + if (queue_len >= 15) { + wpa_printf(MSG_DEBUG, + "SAE: No more room in message queue - drop the new frame from " + MACSTR, MAC2STR(mgmt->sa)); + return; + } + + wpa_printf(MSG_DEBUG, "SAE: Queue Authentication message from " + MACSTR " for processing (queue_len %u)", MAC2STR(mgmt->sa), + queue_len); + q = os_zalloc(sizeof(*q) + len); + if (!q) + return; + q->rssi = rssi; + q->len = len; + os_memcpy(q->msg, mgmt, len); + + /* Check whether there is already a queued Authentication frame from the + * same station with the same transaction number and if so, replace that + * queue entry with the new one. This avoids issues with a peer that + * sends multiple times (e.g., due to frequent SAE retries). There is no + * point in us trying to process the old attempts after a new one has + * obsoleted them. */ + dl_list_for_each(q2, &hapd->sae_commit_queue, + struct hostapd_sae_commit_queue, list) { + mgmt2 = (const struct ieee80211_mgmt *) q2->msg; + if (os_memcmp(mgmt->sa, mgmt2->sa, ETH_ALEN) == 0 && + mgmt->u.auth.auth_transaction == + mgmt2->u.auth.auth_transaction) { + wpa_printf(MSG_DEBUG, + "SAE: Replace queued message from same STA with same transaction number"); + dl_list_add(&q2->list, &q->list); + dl_list_del(&q2->list); + os_free(q2); + goto queued; + } + } + + /* No pending identical entry, so add to the end of the queue */ + dl_list_add_tail(&hapd->sae_commit_queue, &q->list); + +queued: + if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL)) + return; + eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit, + hapd, NULL); +} + + +static int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr) +{ + struct hostapd_sae_commit_queue *q; + const struct ieee80211_mgmt *mgmt; + + dl_list_for_each(q, &hapd->sae_commit_queue, + struct hostapd_sae_commit_queue, list) { + mgmt = (const struct ieee80211_mgmt *) q->msg; + if (os_memcmp(addr, mgmt->sa, ETH_ALEN) == 0) + return 1; + } + + return 0; +} + #endif /* CONFIG_SAE */ @@ -1273,6 +1549,7 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + hapd->iface->freq, elems.rsn_ie - 2, elems.rsn_ie_len + 2, elems.mdie, elems.mdie_len, NULL, 0); resp = wpa_res_to_status_code(res); @@ -1544,7 +1821,10 @@ prepare_auth_resp_fils(struct hostapd_data *hapd, } sta->fils_erp_pmkid_set = 0; - if (wpa_auth_pmksa_add2( + wpa_auth_add_fils_pmk_pmkid(sta->wpa_sm, pmk, pmk_len, + sta->fils_erp_pmkid); + if (!hapd->conf->disable_pmksa_caching && + wpa_auth_pmksa_add2( hapd->wpa_auth, sta->addr, pmk, pmk_len, sta->fils_erp_pmkid, @@ -1743,7 +2023,8 @@ ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta, static void handle_auth(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, size_t len) + const struct ieee80211_mgmt *mgmt, size_t len, + int rssi, int from_queue) { u16 auth_alg, auth_transaction, status_code; u16 resp = WLAN_STATUS_SUCCESS; @@ -1790,11 +2071,12 @@ static void handle_auth(struct hostapd_data *hapd, wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d " "auth_transaction=%d status_code=%d wep=%d%s " - "seq_ctrl=0x%x%s", + "seq_ctrl=0x%x%s%s", MAC2STR(mgmt->sa), auth_alg, auth_transaction, status_code, !!(fc & WLAN_FC_ISWEP), challenge ? " challenge" : "", - seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : ""); + seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "", + from_queue ? " (from queue)" : ""); #ifdef CONFIG_NO_RC4 if (auth_alg == WLAN_AUTH_SHARED_KEY) { @@ -1922,9 +2204,26 @@ static void handle_auth(struct hostapd_data *hapd, if (res == HOSTAPD_ACL_PENDING) return; +#ifdef CONFIG_SAE + if (auth_alg == WLAN_AUTH_SAE && !from_queue && + (auth_transaction == 1 || + (auth_transaction == 2 && auth_sae_queued_addr(hapd, mgmt->sa)))) { + /* Handle SAE Authentication commit message through a queue to + * provide more control for postponing the needed heavy + * processing under a possible DoS attack scenario. In addition, + * queue SAE Authentication confirm message if there happens to + * be a queued commit message from the same peer. This is needed + * to avoid reordering Authentication frames within the same + * SAE exchange. */ + auth_sae_queue(hapd, mgmt, len, rssi); + return; + } +#endif /* CONFIG_SAE */ + sta = ap_get_sta(hapd, mgmt->sa); if (sta) { sta->flags &= ~WLAN_STA_PENDING_FILS_ERP; + sta->ft_over_ds = 0; if ((fc & WLAN_FC_RETRY) && sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && sta->last_seq_ctrl == seq_ctrl && @@ -1973,6 +2272,9 @@ static void handle_auth(struct hostapd_data *hapd, } sta->last_seq_ctrl = seq_ctrl; sta->last_subtype = WLAN_FC_STYPE_AUTH; +#ifdef CONFIG_MBO + sta->auth_rssi = rssi; +#endif /* CONFIG_MBO */ res = ieee802_11_set_radius_info( hapd, sta, res, session_timeout, acct_interim_interval, @@ -2210,6 +2512,59 @@ static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_SUCCESS; } +static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *multi_ap_ie, size_t multi_ap_len) +{ + u8 multi_ap_value = 0; + + sta->flags &= ~WLAN_STA_MULTI_AP; + + if (!hapd->conf->multi_ap) + return WLAN_STATUS_SUCCESS; + + if (multi_ap_ie) { + const u8 *multi_ap_subelem; + + multi_ap_subelem = get_ie(multi_ap_ie + 4, + multi_ap_len - 4, + MULTI_AP_SUB_ELEM_TYPE); + if (multi_ap_subelem && multi_ap_subelem[1] == 1) { + multi_ap_value = multi_ap_subelem[2]; + } else { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Multi-AP IE has missing or invalid Multi-AP subelement"); + return WLAN_STATUS_INVALID_IE; + } + } + + if (multi_ap_value && multi_ap_value != MULTI_AP_BACKHAUL_STA) + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Multi-AP IE with unexpected value 0x%02x", + multi_ap_value); + + if (!(multi_ap_value & MULTI_AP_BACKHAUL_STA)) { + if (hapd->conf->multi_ap & FRONTHAUL_BSS) + return WLAN_STATUS_SUCCESS; + + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Non-Multi-AP STA tries to associate with backhaul-only BSS"); + return WLAN_STATUS_ASSOC_DENIED_UNSPEC; + } + + if (!(hapd->conf->multi_ap & BACKHAUL_BSS)) + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Backhaul STA tries to associate with fronthaul-only BSS"); + + sta->flags |= WLAN_STA_MULTI_AP; + return WLAN_STATUS_SUCCESS; +} + static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta, struct ieee802_11_elems *elems) @@ -2466,6 +2821,11 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, resp = copy_supp_rates(hapd, sta, &elems); if (resp != WLAN_STATUS_SUCCESS) return resp; + + resp = check_multi_ap(hapd, sta, elems.multi_ap, elems.multi_ap_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + #ifdef CONFIG_IEEE80211N resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities); if (resp != WLAN_STATUS_SUCCESS) @@ -2485,6 +2845,10 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, if (resp != WLAN_STATUS_SUCCESS) return resp; + resp = copy_sta_vht_oper(hapd, sta, elems.vht_operation); + 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; @@ -2577,7 +2941,9 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, "state machine"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } + wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg); res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + hapd->iface->freq, wpa_ie, wpa_ie_len, elems.mdie, elems.mdie_len, elems.owe_dh, elems.owe_dh_len); @@ -2669,6 +3035,37 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP2 + dpp_pfs_free(sta->dpp_pfs); + sta->dpp_pfs = NULL; + + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && + hapd->conf->dpp_netaccesskey && sta->wpa_sm && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP && + elems.owe_dh) { + sta->dpp_pfs = dpp_pfs_init( + wpabuf_head(hapd->conf->dpp_netaccesskey), + wpabuf_len(hapd->conf->dpp_netaccesskey)); + if (!sta->dpp_pfs) { + wpa_printf(MSG_DEBUG, + "DPP: Could not initialize PFS"); + /* Try to continue without PFS */ + goto pfs_fail; + } + + if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh, + elems.owe_dh_len) < 0) { + dpp_pfs_free(sta->dpp_pfs); + sta->dpp_pfs = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + } + + wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ? + sta->dpp_pfs->secret : NULL); + pfs_fail: +#endif /* CONFIG_DPP2 */ + #ifdef CONFIG_IEEE80211N if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) && wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) { @@ -2713,10 +3110,20 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, #ifdef CONFIG_HS20 wpabuf_free(sta->hs20_ie); if (elems.hs20 && elems.hs20_len > 4) { + int release; + sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4, elems.hs20_len - 4); - } else + release = ((elems.hs20[4] >> 4) & 0x0f) + 1; + if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm)) { + wpa_printf(MSG_DEBUG, + "HS 2.0: PMF not negotiated by release %d station " + MACSTR, release, MAC2STR(sta->addr)); + return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; + } + } else { sta->hs20_ie = NULL; + } wpabuf_free(sta->roaming_consortium); if (elems.roaming_cons_sel) @@ -2747,6 +3154,35 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_MBO */ +#if defined(CONFIG_FILS) && defined(CONFIG_OCV) + if (wpa_auth_uses_ocv(sta->wpa_sm) && + (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK)) { + struct wpa_channel_info ci; + int tx_chanwidth; + int tx_seg1_idx; + + if (hostapd_drv_channel_info(hapd, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info to validate received OCI in FILS (Re)Association Request frame"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (get_sta_tx_parameters(sta->wpa_sm, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx, &tx_chanwidth, + &tx_seg1_idx) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, + tx_chanwidth, tx_seg1_idx) != 0) { + wpa_printf(MSG_WARNING, "FILS: %s", ocv_errorstr); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + } +#endif /* CONFIG_FILS && CONFIG_OCV */ + ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes, elems.supp_op_classes_len); @@ -2791,7 +3227,7 @@ static void send_deauth(struct hostapd_data *hapd, const u8 *addr, static int add_associated_sta(struct hostapd_data *hapd, - struct sta_info *sta) + struct sta_info *sta, int reassoc) { struct ieee80211_ht_capabilities ht_cap; struct ieee80211_vht_capabilities vht_cap; @@ -2807,14 +3243,36 @@ static int add_associated_sta(struct hostapd_data *hapd, * Skip this if the STA has already completed FT reassociation and the * TK has been configured since the TX/RX PN must not be reset to 0 for * the same key. + * + * FT-over-the-DS has a special case where the STA entry (and as such, + * the TK) has not yet been configured to the driver depending on which + * driver interface is used. For that case, allow add-STA operation to + * be used (instead of set-STA). This is needed to allow mac80211-based + * drivers to accept the STA parameter configuration. Since this is + * after a new FT-over-DS exchange, a new TK has been derived, so key + * reinstallation is not a concern for this case. */ + wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR + " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)", + MAC2STR(sta->addr), sta->added_unassoc, sta->auth_alg, + sta->ft_over_ds, reassoc, + !!(sta->flags & WLAN_STA_AUTHORIZED), + wpa_auth_sta_ft_tk_already_set(sta->wpa_sm), + wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)); + if (!sta->added_unassoc && (!(sta->flags & WLAN_STA_AUTHORIZED) || + (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) || (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) && !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) { hostapd_drv_sta_remove(hapd, sta->addr); wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED); set = 0; + + /* Do not allow the FT-over-DS exception to be used more than + * once per authentication exchange to guarantee a new TK is + * used here */ + sta->ft_over_ds = 0; } #ifdef CONFIG_IEEE80211N @@ -2860,7 +3318,7 @@ static int add_associated_sta(struct hostapd_data *hapd, static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, const u8 *addr, u16 status_code, int reassoc, - const u8 *ies, size_t ies_len) + const u8 *ies, size_t ies_len, int rssi) { int send_len; u8 *buf; @@ -2878,6 +3336,10 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) buflen += 150; #endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP2 + if (sta && sta->dpp_pfs) + buflen += 5 + sta->dpp_pfs->curve->prime_len; +#endif /* CONFIG_DPP2 */ buf = os_zalloc(buflen); if (!buf) { res = WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -2905,6 +3367,16 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, /* Extended supported rates */ p = hostapd_eid_ext_supp_rates(hapd, p); +#ifdef CONFIG_MBO + if (status_code == WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS && + rssi != 0) { + int delta = hapd->iconf->rssi_reject_assoc_rssi - rssi; + + p = hostapd_eid_mbo_rssi_assoc_rej(hapd, p, buf + buflen - p, + delta); + } +#endif /* CONFIG_MBO */ + #ifdef CONFIG_IEEE80211R_AP if (sta && status_code == WLAN_STATUS_SUCCESS) { /* IEEE 802.11r: Mobility Domain Information, Fast BSS @@ -2975,6 +3447,39 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_FST */ +#ifdef CONFIG_OWE + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && + sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) { + struct wpabuf *pub; + + pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); + if (!pub) { + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } + /* OWE Diffie-Hellman Parameter element */ + *p++ = WLAN_EID_EXTENSION; /* Element ID */ + *p++ = 1 + 2 + wpabuf_len(pub); /* Length */ + *p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */ + WPA_PUT_LE16(p, sta->owe_group); + p += 2; + os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub)); + p += wpabuf_len(pub); + wpabuf_free(pub); + } +#endif /* CONFIG_OWE */ + +#ifdef CONFIG_DPP2 + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && + sta && sta->dpp_pfs && status_code == WLAN_STATUS_SUCCESS && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP) { + os_memcpy(p, wpabuf_head(sta->dpp_pfs->ie), + wpabuf_len(sta->dpp_pfs->ie)); + p += wpabuf_len(sta->dpp_pfs->ie); + } +#endif /* CONFIG_DPP2 */ + #ifdef CONFIG_IEEE80211AC if (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT)) p = hostapd_eid_vendor_vht(hapd, p); @@ -2996,6 +3501,9 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_WPS */ + if (sta && (sta->flags & WLAN_STA_MULTI_AP)) + p = hostapd_eid_multi_ap(hapd, p); + #ifdef CONFIG_P2P if (sta && sta->p2p_ie && hapd->p2p_group) { struct wpabuf *p2p_resp_ie; @@ -3068,30 +3576,6 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_FILS */ -#ifdef CONFIG_OWE - if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && - sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS && - wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) { - struct wpabuf *pub; - - pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); - if (!pub) { - res = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto done; - } - /* OWE Diffie-Hellman Parameter element */ - *p++ = WLAN_EID_EXTENSION; /* Element ID */ - *p++ = 1 + 2 + wpabuf_len(pub); /* Length */ - *p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */ - WPA_PUT_LE16(p, sta->owe_group); - p += 2; - os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub)); - p += wpabuf_len(pub); - send_len += 3 + 2 + wpabuf_len(pub); - wpabuf_free(pub); - } -#endif /* CONFIG_OWE */ - if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) { wpa_printf(MSG_INFO, "Failed to send assoc resp: %s", strerror(errno)); @@ -3173,7 +3657,7 @@ void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta) reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS, sta->fils_pending_assoc_is_reassoc, sta->fils_pending_assoc_req, - sta->fils_pending_assoc_req_len); + sta->fils_pending_assoc_req_len, 0); os_free(sta->fils_pending_assoc_req); sta->fils_pending_assoc_req = NULL; sta->fils_pending_assoc_req_len = 0; @@ -3210,7 +3694,7 @@ void fils_hlp_timeout(void *eloop_ctx, void *eloop_data) static void handle_assoc(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, - int reassoc) + int reassoc, int rssi) { u16 capab_info, listen_interval, seq_ctrl, fc; u16 resp = WLAN_STATUS_SUCCESS, reply_res; @@ -3393,6 +3877,14 @@ static void handle_assoc(struct hostapd_data *hapd, resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } + + if (hapd->iconf->rssi_reject_assoc_rssi && rssi && + rssi < hapd->iconf->rssi_reject_assoc_rssi && + (sta->auth_rssi == 0 || + sta->auth_rssi < hapd->iconf->rssi_reject_assoc_rssi)) { + resp = WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS; + goto fail; + } #endif /* CONFIG_MBO */ /* @@ -3550,10 +4042,24 @@ static void handle_assoc(struct hostapd_data *hapd, * issues with processing other non-Data Class 3 frames during this * window. */ - if (resp == WLAN_STATUS_SUCCESS && sta && add_associated_sta(hapd, sta)) + if (resp == WLAN_STATUS_SUCCESS && sta && + add_associated_sta(hapd, sta, reassoc)) resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; #ifdef CONFIG_FILS + if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS && + eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta) && + sta->fils_pending_assoc_req) { + /* Do not reschedule fils_hlp_timeout in case the station + * retransmits (Re)Association Request frame while waiting for + * the previously started FILS HLP wait, so that the timeout can + * be determined from the first pending attempt. */ + wpa_printf(MSG_DEBUG, + "FILS: Continue waiting for HLP processing before sending (Re)Association Response frame to " + MACSTR, MAC2STR(sta->addr)); + os_free(tmp); + return; + } if (sta) { eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); os_free(sta->fils_pending_assoc_req); @@ -3578,7 +4084,7 @@ static void handle_assoc(struct hostapd_data *hapd, #endif /* CONFIG_FILS */ reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc, pos, - left); + left, rssi); os_free(tmp); /* @@ -3718,28 +4224,6 @@ static void handle_beacon(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211W - -static int hostapd_sa_query_action(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, - size_t len) -{ - const u8 *end; - - end = mgmt->u.action.u.sa_query_resp.trans_id + - WLAN_SA_QUERY_TR_ID_LEN; - if (((u8 *) mgmt) + len < end) { - wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action " - "frame (len=%lu)", (unsigned long) len); - return 0; - } - - ieee802_11_sa_query_action(hapd, mgmt->sa, - mgmt->u.action.u.sa_query_resp.action, - mgmt->u.action.u.sa_query_resp.trans_id); - return 1; -} - - static int robust_action_frame(u8 category) { return category != WLAN_ACTION_PUBLIC && @@ -3825,7 +4309,8 @@ static int handle_action(struct hostapd_data *hapd, return 1; #ifdef CONFIG_IEEE80211W case WLAN_ACTION_SA_QUERY: - return hostapd_sa_query_action(hapd, mgmt, len); + ieee802_11_sa_query_action(hapd, mgmt, len); + return 1; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WNM_AP case WLAN_ACTION_WNM: @@ -4020,17 +4505,17 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth"); - handle_auth(hapd, mgmt, len); + handle_auth(hapd, mgmt, len, ssi_signal, 0); ret = 1; break; case WLAN_FC_STYPE_ASSOC_REQ: wpa_printf(MSG_DEBUG, "mgmt::assoc_req"); - handle_assoc(hapd, mgmt, len, 0); + handle_assoc(hapd, mgmt, len, 0, ssi_signal); ret = 1; break; case WLAN_FC_STYPE_REASSOC_REQ: wpa_printf(MSG_DEBUG, "mgmt::reassoc_req"); - handle_assoc(hapd, mgmt, len, 1); + handle_assoc(hapd, mgmt, len, 1, ssi_signal); ret = 1; break; case WLAN_FC_STYPE_DISASSOC: @@ -4236,7 +4721,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd, sta->flags |= WLAN_STA_WDS; } - if (sta->flags & WLAN_STA_WDS) { + if (sta->flags & (WLAN_STA_WDS | WLAN_STA_MULTI_AP)) { int ret; char ifname_wds[IFNAMSIZ + 1]; diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 2f3b4da8e752a..db7badcfffaf6 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -59,6 +59,7 @@ u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid); int hostapd_ht_operation_update(struct hostapd_iface *iface); void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, @@ -80,6 +81,8 @@ 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); +u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_oper); 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, @@ -91,8 +94,8 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, struct sta_info *sta, u8 *eid); void ieee802_11_sa_query_action(struct hostapd_data *hapd, - const u8 *sa, const u8 action_type, - const u8 *trans_id); + const struct ieee80211_mgmt *mgmt, + size_t len); u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid); @@ -120,6 +123,9 @@ u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len); u8 hostapd_mbo_ie_len(struct hostapd_data *hapd); +u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid, + size_t len, int delta); + #else /* CONFIG_MBO */ static inline u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, @@ -166,4 +172,9 @@ int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr, char **identity, char **radius_cui, int is_probe_req); +int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth, + int ap_seg1_idx, int *bandwidth, int *seg1_idx); + +void auth_sae_process_commit(void *eloop_ctx, void *user_ctx); + #endif /* IEEE802_11_H */ diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c index 5cb7fb1454f76..931d4d0659c3c 100644 --- a/src/ap/ieee802_11_auth.c +++ b/src/ap/ieee802_11_auth.c @@ -289,6 +289,9 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, return HOSTAPD_ACL_ACCEPT; }; + if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) + vlan_id = NULL; + /* Check whether ACL cache has an entry for this station */ res = hostapd_acl_cache_get(hapd, addr, session_timeout, acct_interim_interval, vlan_id, psk, @@ -516,7 +519,6 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, struct hostapd_acl_query_data *query, *prev; struct hostapd_cached_radius_acl *cache; struct radius_hdr *hdr = radius_msg_get_hdr(msg); - int *untagged, *tagged, *notempty; query = hapd->acl_queries; prev = NULL; @@ -574,12 +576,10 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, cache->acct_interim_interval = 0; } - notempty = &cache->vlan_id.notempty; - untagged = &cache->vlan_id.untagged; - tagged = cache->vlan_id.tagged; - *notempty = !!radius_msg_get_vlanid(msg, untagged, - MAX_NUM_TAGGED_VLAN, - tagged); + if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED) + cache->vlan_id.notempty = !!radius_msg_get_vlanid( + msg, &cache->vlan_id.untagged, + MAX_NUM_TAGGED_VLAN, cache->vlan_id.tagged); decode_tunnel_passwords(hapd, shared_secret, shared_secret_len, msg, req, cache); diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c index 1a8d469729856..072135863682e 100644 --- a/src/ap/ieee802_11_he.c +++ b/src/ap/ieee802_11_he.c @@ -86,3 +86,34 @@ u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid) return pos; } + + +u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_he_mu_edca_parameter_set *edca; + u8 *pos; + size_t i; + + pos = (u8 *) &hapd->iface->conf->he_mu_edca; + for (i = 0; i < sizeof(*edca); i++) { + if (pos[i]) + break; + } + if (i == sizeof(*edca)) + return eid; /* no MU EDCA Parameters configured */ + + pos = eid; + *pos++ = WLAN_EID_EXTENSION; + *pos++ = 1 + sizeof(*edca); + *pos++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS; + + edca = (struct ieee80211_he_mu_edca_parameter_set *) pos; + os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca)); + + wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element", + pos, sizeof(*edca)); + + pos += sizeof(*edca); + + return pos; +} diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index 49e9bf8cc7c9c..707381ffe7099 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -10,10 +10,12 @@ #include "utils/common.h" #include "common/ieee802_11_defs.h" +#include "common/ocv.h" #include "hostapd.h" #include "sta_info.h" #include "ap_config.h" #include "ap_drv_ops.h" +#include "wpa_auth.h" #include "ieee802_11.h" @@ -49,7 +51,12 @@ u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, const u8 *addr, const u8 *trans_id) { - struct ieee80211_mgmt mgmt; +#ifdef CONFIG_OCV + struct sta_info *sta; +#endif /* CONFIG_OCV */ + struct ieee80211_mgmt *mgmt; + u8 *oci_ie = NULL; + u8 oci_ie_len = 0; u8 *end; wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to " @@ -57,19 +64,61 @@ void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", trans_id, WLAN_SA_QUERY_TR_ID_LEN); - os_memset(&mgmt, 0, sizeof(mgmt)); - mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - os_memcpy(mgmt.da, addr, ETH_ALEN); - os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); - os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); - mgmt.u.action.category = WLAN_ACTION_SA_QUERY; - mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; - os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id, +#ifdef CONFIG_OCV + sta = ap_get_sta(hapd, addr); + if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) { + struct wpa_channel_info ci; + + if (hostapd_drv_channel_info(hapd, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element in SA Query Request"); + return; + } + + oci_ie_len = OCV_OCI_EXTENDED_LEN; + oci_ie = os_zalloc(oci_ie_len); + if (!oci_ie) { + wpa_printf(MSG_WARNING, + "Failed to allocate buffer for OCI element in SA Query Request"); + return; + } + + if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { + os_free(oci_ie); + return; + } + } +#endif /* CONFIG_OCV */ + + mgmt = os_zalloc(sizeof(*mgmt) + oci_ie_len); + if (!mgmt) { + wpa_printf(MSG_DEBUG, + "Failed to allocate buffer for SA Query Response frame"); + os_free(oci_ie); + return; + } + + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt->da, addr, ETH_ALEN); + os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); + mgmt->u.action.category = WLAN_ACTION_SA_QUERY; + mgmt->u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; + os_memcpy(mgmt->u.action.u.sa_query_req.trans_id, trans_id, WLAN_SA_QUERY_TR_ID_LEN); - end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; - if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0) + end = mgmt->u.action.u.sa_query_req.variable; +#ifdef CONFIG_OCV + if (oci_ie_len > 0) { + os_memcpy(end, oci_ie, oci_ie_len); + end += oci_ie_len; + } +#endif /* CONFIG_OCV */ + if (hostapd_drv_send_mlme(hapd, mgmt, end - (u8 *) mgmt, 0) < 0) wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed"); + + os_free(mgmt); + os_free(oci_ie); } @@ -77,7 +126,9 @@ static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd, const u8 *sa, const u8 *trans_id) { struct sta_info *sta; - struct ieee80211_mgmt resp; + struct ieee80211_mgmt *resp; + u8 *oci_ie = NULL; + u8 oci_ie_len = 0; u8 *end; wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from " @@ -92,30 +143,123 @@ static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd, return; } +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(sta->wpa_sm)) { + struct wpa_channel_info ci; + + if (hostapd_drv_channel_info(hapd, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element in SA Query Response"); + return; + } + + oci_ie_len = OCV_OCI_EXTENDED_LEN; + oci_ie = os_zalloc(oci_ie_len); + if (!oci_ie) { + wpa_printf(MSG_WARNING, + "Failed to allocate buffer for for OCI element in SA Query Response"); + return; + } + + if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { + os_free(oci_ie); + return; + } + } +#endif /* CONFIG_OCV */ + + resp = os_zalloc(sizeof(*resp) + oci_ie_len); + if (!resp) { + wpa_printf(MSG_DEBUG, + "Failed to allocate buffer for SA Query Response frame"); + os_free(oci_ie); + return; + } + wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to " MACSTR, MAC2STR(sa)); - os_memset(&resp, 0, sizeof(resp)); - resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - os_memcpy(resp.da, sa, ETH_ALEN); - os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN); - os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN); - resp.u.action.category = WLAN_ACTION_SA_QUERY; - resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE; - os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id, + resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(resp->da, sa, ETH_ALEN); + os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); + resp->u.action.category = WLAN_ACTION_SA_QUERY; + resp->u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE; + os_memcpy(resp->u.action.u.sa_query_req.trans_id, trans_id, WLAN_SA_QUERY_TR_ID_LEN); - end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; - if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp, 0) < 0) + end = resp->u.action.u.sa_query_req.variable; +#ifdef CONFIG_OCV + if (oci_ie_len > 0) { + os_memcpy(end, oci_ie, oci_ie_len); + end += oci_ie_len; + } +#endif /* CONFIG_OCV */ + if (hostapd_drv_send_mlme(hapd, resp, end - (u8 *) resp, 0) < 0) wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed"); + + os_free(resp); + os_free(oci_ie); } -void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa, - const u8 action_type, const u8 *trans_id) +void ieee802_11_sa_query_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len) { struct sta_info *sta; int i; + const u8 *sa = mgmt->sa; + const u8 action_type = mgmt->u.action.u.sa_query_resp.action; + const u8 *trans_id = mgmt->u.action.u.sa_query_resp.trans_id; + + if (((const u8 *) mgmt) + len < + mgmt->u.action.u.sa_query_resp.variable) { + wpa_printf(MSG_DEBUG, + "IEEE 802.11: Too short SA Query Action frame (len=%lu)", + (unsigned long) len); + return; + } + + sta = ap_get_sta(hapd, sa); + +#ifdef CONFIG_OCV + if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) { + struct ieee802_11_elems elems; + struct wpa_channel_info ci; + int tx_chanwidth; + int tx_seg1_idx; + size_t ies_len; + const u8 *ies; + + ies = mgmt->u.action.u.sa_query_resp.variable; + ies_len = len - (ies - (u8 *) mgmt); + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == + ParseFailed) { + wpa_printf(MSG_DEBUG, + "SA Query: Failed to parse elements"); + return; + } + + if (hostapd_drv_channel_info(hapd, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info to validate received OCI in SA Query Action frame"); + return; + } + + if (get_sta_tx_parameters(sta->wpa_sm, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx, &tx_chanwidth, + &tx_seg1_idx) < 0) + return; + + if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, + tx_chanwidth, tx_seg1_idx) != 0) { + wpa_printf(MSG_WARNING, "%s", ocv_errorstr); + return; + } + } +#endif /* CONFIG_OCV */ if (action_type == WLAN_SA_QUERY_REQUEST) { ieee802_11_send_sa_query_resp(hapd, sa, trans_id); @@ -135,7 +279,6 @@ void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa, /* MLME-SAQuery.confirm */ - sta = ap_get_sta(hapd, sa); if (sta == NULL || sta->sa_query_trans_id == NULL) { wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with " "pending SA Query request found"); @@ -237,6 +380,21 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) *pos |= 0x01; #endif /* CONFIG_FILS */ break; + case 10: /* Bits 80-87 */ +#ifdef CONFIG_SAE + if (hapd->conf->wpa && + wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt)) { + int in_use = hostapd_sae_pw_id_in_use(hapd->conf); + + if (in_use) + *pos |= 0x02; /* Bit 81 - SAE Password + * Identifiers In Use */ + if (in_use == 2) + *pos |= 0x04; /* Bit 82 - SAE Password + * Identifiers Used Exclusively */ + } +#endif /* CONFIG_SAE */ + break; } } @@ -276,6 +434,12 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) && len < 10) len = 10; #endif /* CONFIG_FILS */ +#ifdef CONFIG_SAE + if (len < 11 && hapd->conf->wpa && + wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && + hostapd_sae_pw_id_in_use(hapd->conf)) + len = 11; +#endif /* CONFIG_SAE */ if (len < hapd->iface->extended_capa_len) len = hapd->iface->extended_capa_len; if (len == 0) @@ -547,6 +711,22 @@ u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) #ifdef CONFIG_MBO +u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid, + size_t len, int delta) +{ + u8 mbo[4]; + + mbo[0] = OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT; + mbo[1] = 2; + /* Delta RSSI */ + mbo[2] = delta; + /* Retry delay */ + mbo[3] = hapd->iconf->rssi_reject_assoc_timeout; + + return eid + mbo_add_ie(eid, len, mbo, 4); +} + + u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len) { u8 mbo[9], *mbo_pos = mbo; @@ -752,3 +932,71 @@ u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid) return pos; } + + +#ifdef CONFIG_OCV +int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth, + int ap_seg1_idx, int *bandwidth, int *seg1_idx) +{ + int ht_40mhz = 0; + int vht_80p80 = 0; + int requested_bw; + + if (sta->ht_capabilities) + ht_40mhz = !!(sta->ht_capabilities->ht_capabilities_info & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET); + + if (sta->vht_operation) { + struct ieee80211_vht_operation *oper = sta->vht_operation; + + /* + * If a VHT Operation element was present, use it to determine + * the supported channel bandwidth. + */ + if (oper->vht_op_info_chwidth == 0) { + requested_bw = ht_40mhz ? 40 : 20; + } else if (oper->vht_op_info_chan_center_freq_seg1_idx == 0) { + requested_bw = 80; + } else { + int diff; + + requested_bw = 160; + diff = abs((int) + oper->vht_op_info_chan_center_freq_seg0_idx - + (int) + oper->vht_op_info_chan_center_freq_seg1_idx); + vht_80p80 = oper->vht_op_info_chan_center_freq_seg1_idx + != 0 && diff > 16; + } + } else if (sta->vht_capabilities) { + struct ieee80211_vht_capabilities *capab; + int vht_chanwidth; + + capab = sta->vht_capabilities; + + /* + * If only the VHT Capabilities element is present (e.g., for + * normal clients), use it to determine the supported channel + * bandwidth. + */ + vht_chanwidth = capab->vht_capabilities_info & + VHT_CAP_SUPP_CHAN_WIDTH_MASK; + vht_80p80 = capab->vht_capabilities_info & + VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + + /* TODO: Also take into account Extended NSS BW Support field */ + requested_bw = vht_chanwidth ? 160 : 80; + } else { + requested_bw = ht_40mhz ? 40 : 20; + } + + *bandwidth = requested_bw < ap_max_chanwidth ? + requested_bw : ap_max_chanwidth; + + *seg1_idx = 0; + if (ap_seg1_idx && vht_80p80) + *seg1_idx = ap_seg1_idx; + + return 0; +} +#endif /* CONFIG_OCV */ diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c index 8d0662078bcf1..54ee080a43f02 100644 --- a/src/ap/ieee802_11_vht.c +++ b/src/ap/ieee802_11_vht.c @@ -357,6 +357,29 @@ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, } +u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_oper) +{ + if (!vht_oper) { + os_free(sta->vht_operation); + sta->vht_operation = NULL; + return WLAN_STATUS_SUCCESS; + } + + if (!sta->vht_operation) { + sta->vht_operation = + os_zalloc(sizeof(struct ieee80211_vht_operation)); + if (!sta->vht_operation) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + os_memcpy(sta->vht_operation, vht_oper, + sizeof(struct ieee80211_vht_operation)); + + return WLAN_STATUS_SUCCESS; +} + + u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ie, size_t len) { diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index 185279f9e3ef1..97f503f75cc3d 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -1,6 +1,6 @@ /* * hostapd / IEEE 802.1X-2004 Authenticator - * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -682,9 +682,8 @@ void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, #ifdef CONFIG_HS20 if (hapd->conf->hs20) { - u8 ver = 1; /* Release 2 */ - if (HS20_VERSION > 0x10) - ver = 2; /* Release 3 */ + u8 ver = hapd->conf->hs20_release - 1; + if (!radius_msg_add_wfa( msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION, &ver, 1)) { @@ -1237,6 +1236,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) sta->eapol_sm->portValid = TRUE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); + wpa_auth_set_ptk_rekey_timer(sta->wpa_sm); return; } #endif /* CONFIG_FILS */ @@ -1743,6 +1743,45 @@ ieee802_1x_search_radius_identifier(struct hostapd_data *hapd, u8 identifier) } +#ifndef CONFIG_NO_VLAN +static int ieee802_1x_update_vlan(struct radius_msg *msg, + struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct vlan_description vlan_desc; + + os_memset(&vlan_desc, 0, sizeof(vlan_desc)); + vlan_desc.notempty = !!radius_msg_get_vlanid(msg, &vlan_desc.untagged, + MAX_NUM_TAGGED_VLAN, + vlan_desc.tagged); + + if (vlan_desc.notempty && + !hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) { + sta->eapol_sm->authFail = TRUE; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "Invalid VLAN %d%s received from RADIUS server", + vlan_desc.untagged, + vlan_desc.tagged[0] ? "+" : ""); + os_memset(&vlan_desc, 0, sizeof(vlan_desc)); + ap_sta_set_vlan(hapd, sta, &vlan_desc); + return -1; + } + + if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED && + !vlan_desc.notempty) { + sta->eapol_sm->authFail = TRUE; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_INFO, + "authentication server did not include required VLAN ID in Access-Accept"); + return -1; + } + + return ap_sta_set_vlan(hapd, sta, &vlan_desc); +} +#endif /* CONFIG_NO_VLAN */ + + /** * ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server * @msg: RADIUS response message @@ -1765,12 +1804,6 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, struct eapol_state_machine *sm; int override_eapReq = 0; struct radius_hdr *hdr = radius_msg_get_hdr(msg); - struct vlan_description vlan_desc; -#ifndef CONFIG_NO_VLAN - int *untagged, *tagged, *notempty; -#endif /* CONFIG_NO_VLAN */ - - os_memset(&vlan_desc, 0, sizeof(vlan_desc)); sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier); if (sm == NULL) { @@ -1835,56 +1868,21 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, switch (hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: #ifndef CONFIG_NO_VLAN - if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED) { - notempty = &vlan_desc.notempty; - untagged = &vlan_desc.untagged; - tagged = vlan_desc.tagged; - *notempty = !!radius_msg_get_vlanid(msg, untagged, - MAX_NUM_TAGGED_VLAN, - tagged); - } - - if (vlan_desc.notempty && - !hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) { - sta->eapol_sm->authFail = TRUE; - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_INFO, - "Invalid VLAN %d%s received from RADIUS server", - vlan_desc.untagged, - vlan_desc.tagged[0] ? "+" : ""); - os_memset(&vlan_desc, 0, sizeof(vlan_desc)); - ap_sta_set_vlan(hapd, sta, &vlan_desc); - break; - } - - if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED && - !vlan_desc.notempty) { - sta->eapol_sm->authFail = TRUE; - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_INFO, "authentication " - "server did not include required VLAN " - "ID in Access-Accept"); - break; - } -#endif /* CONFIG_NO_VLAN */ - - if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0) + if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED && + ieee802_1x_update_vlan(msg, hapd, sta) < 0) break; -#ifndef CONFIG_NO_VLAN if (sta->vlan_id > 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); } -#endif /* CONFIG_NO_VLAN */ if ((sta->flags & WLAN_STA_ASSOC) && ap_sta_bind_vlan(hapd, sta) < 0) break; +#endif /* CONFIG_NO_VLAN */ sta->session_timeout_set = !!session_timeout_set; os_get_reltime(&sta->session_timeout); @@ -2597,6 +2595,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, struct os_reltime diff; const char *name1; const char *name2; + char *identity_buf = NULL; if (sm == NULL) return 0; @@ -2712,6 +2711,14 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, /* dot1xAuthSessionStatsTable */ os_reltime_age(&sta->acct_session_start, &diff); + if (sm->eap && !sm->identity) { + const u8 *id; + size_t id_len; + + id = eap_get_identity(sm->eap, &id_len); + if (id) + identity_buf = dup_binstr(id, id_len); + } ret = os_snprintf(buf + len, buflen - len, /* TODO: dot1xAuthSessionOctetsRx */ /* TODO: dot1xAuthSessionOctetsTx */ @@ -2727,7 +2734,9 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? 1 : 2, (unsigned int) diff.sec, - sm->identity); + sm->identity ? (char *) sm->identity : + (identity_buf ? identity_buf : "N/A")); + os_free(identity_buf); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c index b8fd5924b8893..2b6f72726b64e 100644 --- a/src/ap/neighbor_db.c +++ b/src/ap/neighbor_db.c @@ -11,6 +11,7 @@ #include "utils/common.h" #include "hostapd.h" +#include "ieee802_11.h" #include "neighbor_db.h" @@ -123,7 +124,7 @@ int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid, } -void hostpad_free_neighbor_db(struct hostapd_data *hapd) +void hostapd_free_neighbor_db(struct hostapd_data *hapd) { struct hostapd_neighbor_entry *nr, *prev; @@ -134,3 +135,123 @@ void hostpad_free_neighbor_db(struct hostapd_data *hapd) os_free(nr); } } + + +#ifdef NEED_AP_MLME +static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd, + int ht, int vht) +{ + if (!ht && !vht) + return NR_CHAN_WIDTH_20; + if (!hapd->iconf->secondary_channel) + return NR_CHAN_WIDTH_20; + if (!vht || hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT) + return NR_CHAN_WIDTH_40; + if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ) + return NR_CHAN_WIDTH_80; + if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_160MHZ) + return NR_CHAN_WIDTH_160; + if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) + return NR_CHAN_WIDTH_80P80; + return NR_CHAN_WIDTH_20; +} +#endif /* NEED_AP_MLME */ + + +void hostapd_neighbor_set_own_report(struct hostapd_data *hapd) +{ +#ifdef NEED_AP_MLME + u16 capab = hostapd_own_capab_info(hapd); + int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n; + int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac; + struct wpa_ssid_value ssid; + u8 channel, op_class; + u8 center_freq1_idx = 0, center_freq2_idx = 0; + enum nr_chan_width width; + u32 bssid_info; + struct wpabuf *nr; + + if (!(hapd->conf->radio_measurements[0] & + WLAN_RRM_CAPS_NEIGHBOR_REPORT)) + return; + + bssid_info = 3; /* AP is reachable */ + bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */ + bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */ + + if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) + bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT; + + bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */ + + if (hapd->conf->wmm_enabled) { + bssid_info |= NEI_REP_BSSID_INFO_QOS; + + if (hapd->conf->wmm_uapsd && + (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD)) + bssid_info |= NEI_REP_BSSID_INFO_APSD; + } + + if (ht) { + bssid_info |= NEI_REP_BSSID_INFO_HT | + NEI_REP_BSSID_INFO_DELAYED_BA; + + /* VHT bit added in IEEE P802.11-REVmc/D4.3 */ + if (vht) + bssid_info |= NEI_REP_BSSID_INFO_VHT; + } + + /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */ + + if (ieee80211_freq_to_channel_ext(hapd->iface->freq, + hapd->iconf->secondary_channel, + hapd->iconf->vht_oper_chwidth, + &op_class, &channel) == + NUM_HOSTAPD_MODES) + return; + width = hostapd_get_nr_chan_width(hapd, ht, vht); + if (vht) { + center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx; + if (width == NR_CHAN_WIDTH_80P80) + center_freq2_idx = + hapd->iconf->vht_oper_centr_freq_seg1_idx; + } else if (ht) { + ieee80211_freq_to_chan(hapd->iface->freq + + 10 * hapd->iconf->secondary_channel, + ¢er_freq1_idx); + } + + ssid.ssid_len = hapd->conf->ssid.ssid_len; + os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len); + + /* + * Neighbor Report element size = BSSID + BSSID info + op_class + chan + + * phy type + wide bandwidth channel subelement. + */ + nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5); + if (!nr) + return; + + wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN); + wpabuf_put_le32(nr, bssid_info); + wpabuf_put_u8(nr, op_class); + wpabuf_put_u8(nr, channel); + wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht)); + + /* + * Wide Bandwidth Channel subelement may be needed to allow the + * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0 + * Figure 9-301. + */ + wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN); + wpabuf_put_u8(nr, 3); + wpabuf_put_u8(nr, width); + wpabuf_put_u8(nr, center_freq1_idx); + wpabuf_put_u8(nr, center_freq2_idx); + + hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci, + hapd->iconf->civic, hapd->iconf->stationary_ap); + + wpabuf_free(nr); +#endif /* NEED_AP_MLME */ +} diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h index ba46d88435e56..9c8f4f2dd69d6 100644 --- a/src/ap/neighbor_db.h +++ b/src/ap/neighbor_db.h @@ -17,8 +17,9 @@ int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid, const struct wpa_ssid_value *ssid, const struct wpabuf *nr, const struct wpabuf *lci, const struct wpabuf *civic, int stationary); +void hostapd_neighbor_set_own_report(struct hostapd_data *hapd); int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid, const struct wpa_ssid_value *ssid); -void hostpad_free_neighbor_db(struct hostapd_data *hapd); +void hostapd_free_neighbor_db(struct hostapd_data *hapd); #endif /* NEIGHBOR_DB_H */ diff --git a/src/ap/rrm.c b/src/ap/rrm.c index 56ed29c1c0687..f2d5cd16e8858 100644 --- a/src/ap/rrm.c +++ b/src/ap/rrm.c @@ -558,7 +558,7 @@ int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr, void hostapd_clean_rrm(struct hostapd_data *hapd) { - hostpad_free_neighbor_db(hapd); + hostapd_free_neighbor_db(hapd); eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL); hapd->lci_req_active = 0; eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL); diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 179cf43b60b17..4f9eae8477b63 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -13,6 +13,7 @@ #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" #include "common/sae.h" +#include "common/dpp.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" @@ -166,7 +167,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) /* just in case */ ap_sta_set_authorized(hapd, sta, 0); - if (sta->flags & WLAN_STA_WDS) + if (sta->flags & (WLAN_STA_WDS | WLAN_STA_MULTI_AP)) hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0); if (sta->ipaddr) @@ -328,6 +329,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) os_free(sta->ht_capabilities); os_free(sta->vht_capabilities); + os_free(sta->vht_operation); hostapd_free_psk_list(sta->psk); os_free(sta->identity); os_free(sta->radius_cui); @@ -361,6 +363,11 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) crypto_ecdh_deinit(sta->owe_ecdh); #endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP2 + dpp_pfs_free(sta->dpp_pfs); + sta->dpp_pfs = NULL; +#endif /* CONFIG_DPP2 */ + os_free(sta->ext_capability); #ifdef CONFIG_WNM_AP @@ -500,6 +507,13 @@ skip_poll: } else if (sta->timeout_next != STA_REMOVE) { int deauth = sta->timeout_next == STA_DEAUTH; + if (!deauth && !(sta->flags & WLAN_STA_ASSOC)) { + /* Cannot disassociate not-associated STA, so move + * directly to deauthentication. */ + sta->timeout_next = STA_DEAUTH; + deauth = 1; + } + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Timeout, sending %s info to STA " MACSTR, deauth ? "deauthentication" : "disassociation", @@ -798,6 +812,8 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, ap_handle_timer, hapd, sta); accounting_sta_stop(hapd, sta); ieee802_1x_free_station(hapd, sta); + wpa_auth_sta_deinit(sta->wpa_sm); + sta->wpa_sm = NULL; sta->disassoc_reason = reason; sta->flags |= WLAN_STA_PENDING_DISASSOC_CB; @@ -896,9 +912,6 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, struct hostapd_vlan *vlan = NULL, *wildcard_vlan = NULL; int old_vlan_id, vlan_id = 0, ret = 0; - if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) - vlan_desc = NULL; - /* Check if there is something to do */ if (hapd->conf->ssid.per_sta_vif && !sta->vlan_id) { /* This sta is lacking its own vif */ @@ -1165,6 +1178,32 @@ void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta) #endif /* CONFIG_IEEE80211W */ +const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct hostapd_wpa_psk *psk; + struct hostapd_ssid *ssid; + const u8 *pmk; + int pmk_len; + + ssid = &hapd->conf->ssid; + + pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len); + if (!pmk || pmk_len != PMK_LEN) + return NULL; + + for (psk = ssid->wpa_psk; psk; psk = psk->next) + if (os_memcmp(pmk, psk->psk, PMK_LEN) == 0) + break; + if (!psk) + return NULL; + if (!psk || !psk->keyid[0]) + return NULL; + + return psk->keyid; +} + + void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized) { @@ -1203,7 +1242,11 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, sta->addr, authorized, dev_addr); if (authorized) { + const char *keyid; + char keyid_buf[100]; char ip_addr[100]; + + keyid_buf[0] = '\0'; ip_addr[0] = '\0'; #ifdef CONFIG_P2P if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) { @@ -1214,14 +1257,20 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_P2P */ - wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s", - buf, ip_addr); + keyid = ap_sta_wpa_get_keyid(hapd, sta); + if (keyid) { + os_snprintf(keyid_buf, sizeof(keyid_buf), + " keyid=%s", keyid); + } + + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s", + buf, ip_addr, keyid_buf); if (hapd->msg_ctx_parent && hapd->msg_ctx_parent != hapd->msg_ctx) wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO, - AP_STA_CONNECTED "%s%s", - buf, ip_addr); + AP_STA_CONNECTED "%s%s%s", + buf, ip_addr, keyid_buf); } else { wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf); diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 9cac6f157f68f..ece0c60abd364 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -36,6 +36,7 @@ #define WLAN_STA_VHT_OPMODE_ENABLED BIT(20) #define WLAN_STA_VENDOR_VHT BIT(21) #define WLAN_STA_PENDING_FILS_ERP BIT(22) +#define WLAN_STA_MULTI_AP BIT(23) #define WLAN_STA_PENDING_DISASSOC_CB BIT(29) #define WLAN_STA_PENDING_DEAUTH_CB BIT(30) #define WLAN_STA_NONERP BIT(31) @@ -117,6 +118,7 @@ struct sta_info { unsigned int power_capab:1; unsigned int agreed_to_steer:1; unsigned int hs20_t_c_filtering:1; + unsigned int ft_over_ds:1; u16 auth_alg; @@ -162,6 +164,7 @@ struct sta_info { struct ieee80211_ht_capabilities *ht_capabilities; struct ieee80211_vht_capabilities *vht_capabilities; + struct ieee80211_vht_operation *vht_operation; u8 vht_opmode; #ifdef CONFIG_IEEE80211W @@ -215,6 +218,7 @@ struct sta_info { u8 cell_capa; /* 0 = unknown (not an MBO STA); otherwise, * enum mbo_cellular_capa values */ struct mbo_non_pref_chan_info *non_pref_chan; + int auth_rssi; /* Last Authentication frame RSSI */ #endif /* CONFIG_MBO */ u8 *supp_op_classes; /* Supported Operating Classes element, if @@ -261,6 +265,10 @@ struct sta_info { u8 *ext_capability; char *ifname_wds; /* WDS ifname, if in use */ +#ifdef CONFIG_DPP2 + struct dpp_pfs *dpp_pfs; +#endif /* CONFIG_DPP2 */ + #ifdef CONFIG_TESTING_OPTIONS enum wpa_alg last_tk_alg; int last_tk_key_idx; @@ -320,6 +328,8 @@ int ap_sta_set_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); +const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd, + struct sta_info *sta); void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, const u8 *addr, u16 reason); diff --git a/src/ap/vlan_full.c b/src/ap/vlan_full.c index aa42335b96a19..19aa3c649a5db 100644 --- a/src/ap/vlan_full.c +++ b/src/ap/vlan_full.c @@ -16,6 +16,7 @@ #include "utils/common.h" #include "drivers/priv_netlink.h" +#include "drivers/linux_ioctl.h" #include "common/linux_bridge.h" #include "common/linux_vlan.h" #include "utils/eloop.h" @@ -143,6 +144,9 @@ static int br_delif(const char *br_name, const char *if_name) return -1; } + if (linux_br_del_if(fd, br_name, if_name) == 0) + goto done; + if_index = if_nametoindex(if_name); if (if_index == 0) { @@ -168,6 +172,7 @@ static int br_delif(const char *br_name, const char *if_name) return -1; } +done: close(fd); return 0; } @@ -194,6 +199,14 @@ static int br_addif(const char *br_name, const char *if_name) return -1; } + if (linux_br_add_if(fd, br_name, if_name) == 0) + goto done; + if (errno == EBUSY) { + /* The interface is already added. */ + close(fd); + return 1; + } + if_index = if_nametoindex(if_name); if (if_index == 0) { @@ -224,6 +237,7 @@ static int br_addif(const char *br_name, const char *if_name) return -1; } +done: close(fd); return 0; } @@ -241,6 +255,9 @@ static int br_delbr(const char *br_name) return -1; } + if (linux_br_del(fd, br_name) == 0) + goto done; + arg[0] = BRCTL_DEL_BRIDGE; arg[1] = (unsigned long) br_name; @@ -252,6 +269,7 @@ static int br_delbr(const char *br_name) return -1; } +done: close(fd); return 0; } @@ -277,11 +295,19 @@ static int br_addbr(const char *br_name) return -1; } + if (linux_br_add(fd, br_name) == 0) + goto done; + if (errno == EEXIST) { + /* The bridge is already added. */ + close(fd); + return 1; + } + arg[0] = BRCTL_ADD_BRIDGE; arg[1] = (unsigned long) br_name; if (ioctl(fd, SIOCGIFBR, arg) < 0) { - if (errno == EEXIST) { + if (errno == EEXIST) { /* The bridge is already added. */ close(fd); return 1; @@ -294,6 +320,7 @@ static int br_addbr(const char *br_name) } } +done: /* Decrease forwarding delay to avoid EAPOL timeouts. */ os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ); @@ -363,12 +390,18 @@ static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface, { char vlan_ifname[IFNAMSIZ]; int clean; + int ret; if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE) - os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", - tagged_interface, vid); + ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", + tagged_interface, vid); else - os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid); + ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", + vid); + if (ret >= (int) sizeof(vlan_ifname)) + wpa_printf(MSG_WARNING, + "VLAN: Interface name was truncated to %s", + vlan_ifname); clean = 0; ifconfig_up(tagged_interface); @@ -384,19 +417,28 @@ static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface, } -static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd, int vid) +static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd, + struct hostapd_vlan *vlan, int vid) { char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; - - if (hapd->conf->vlan_bridge[0]) { - os_snprintf(br_name, IFNAMSIZ, "%s%d", - hapd->conf->vlan_bridge, vid); + int ret; + + if (vlan->bridge[0]) { + os_strlcpy(br_name, vlan->bridge, IFNAMSIZ); + ret = 0; + } else if (hapd->conf->vlan_bridge[0]) { + ret = os_snprintf(br_name, IFNAMSIZ, "%s%d", + hapd->conf->vlan_bridge, vid); } else if (tagged_interface) { - os_snprintf(br_name, IFNAMSIZ, "br%s.%d", - tagged_interface, vid); + ret = os_snprintf(br_name, IFNAMSIZ, "br%s.%d", + tagged_interface, vid); } else { - os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid); + ret = os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid); } + if (ret >= IFNAMSIZ) + wpa_printf(MSG_WARNING, + "VLAN: Interface name was truncated to %s", + br_name); } @@ -445,7 +487,7 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd) !br_addif(hapd->conf->bridge, ifname)) vlan->clean |= DVLAN_CLEAN_WLAN_PORT; } else if (untagged > 0 && untagged <= MAX_VLAN_ID) { - vlan_bridge_name(br_name, hapd, untagged); + vlan_bridge_name(br_name, hapd, vlan, untagged); vlan_get_bridge(br_name, hapd, untagged); @@ -458,7 +500,7 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd) tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID || (i > 0 && tagged[i] == tagged[i - 1])) continue; - vlan_bridge_name(br_name, hapd, tagged[i]); + vlan_bridge_name(br_name, hapd, vlan, tagged[i]); vlan_get_bridge(br_name, hapd, tagged[i]); vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE, ifname, br_name, tagged[i], hapd); @@ -474,12 +516,19 @@ static void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface, { char vlan_ifname[IFNAMSIZ]; int clean; + int ret; if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE) - os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", - tagged_interface, vid); + ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", + tagged_interface, vid); else - os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid); + ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", + vid); + if (ret >= (int) sizeof(vlan_ifname)) + wpa_printf(MSG_WARNING, + "VLAN: Interface name was truncated to %s", + vlan_ifname); + clean = dyn_iface_put(hapd, vlan_ifname); @@ -543,7 +592,7 @@ void vlan_dellink(const char *ifname, struct hostapd_data *hapd) tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID || (i > 0 && tagged[i] == tagged[i - 1])) continue; - vlan_bridge_name(br_name, hapd, tagged[i]); + vlan_bridge_name(br_name, hapd, vlan, tagged[i]); vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE, ifname, br_name, tagged[i], hapd); vlan_put_bridge(br_name, hapd, tagged[i]); @@ -555,7 +604,7 @@ void vlan_dellink(const char *ifname, struct hostapd_data *hapd) (vlan->clean & DVLAN_CLEAN_WLAN_PORT)) br_delif(hapd->conf->bridge, ifname); } else if (untagged > 0 && untagged <= MAX_VLAN_ID) { - vlan_bridge_name(br_name, hapd, untagged); + vlan_bridge_name(br_name, hapd, vlan, untagged); if (vlan->clean & DVLAN_CLEAN_WLAN_PORT) br_delif(br_name, vlan->ifname); diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c index 01fecee026a11..e293a003303f9 100644 --- a/src/ap/vlan_init.c +++ b/src/ap/vlan_init.c @@ -187,6 +187,7 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, { struct hostapd_vlan *n; char ifname[IFNAMSIZ + 1], *pos; + int ret; if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD) return NULL; @@ -208,8 +209,13 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, n->vlan_desc = *vlan_desc; n->dynamic_vlan = 1; - os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id, - pos); + ret = os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", + ifname, vlan_id, pos); + if (os_snprintf_error(sizeof(n->ifname), ret)) { + os_free(n); + return NULL; + } + os_strlcpy(n->bridge, vlan->bridge, sizeof(n->bridge)); n->next = hapd->conf->vlan; hapd->conf->vlan = n; diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c index 1e8f58b4d07ee..27c69d34a7caa 100644 --- a/src/ap/wnm_ap.c +++ b/src/ap/wnm_ap.c @@ -12,6 +12,7 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" +#include "common/ocv.h" #include "ap/hostapd.h" #include "ap/sta_info.h" #include "ap/ap_config.h" @@ -54,8 +55,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, size_t gtk_elem_len = 0; size_t igtk_elem_len = 0; struct wnm_sleep_element wnmsleep_ie; - u8 *wnmtfs_ie; - u8 wnmsleep_ie_len; + u8 *wnmtfs_ie, *oci_ie; + u8 wnmsleep_ie_len, oci_ie_len; u16 wnmtfs_ie_len; u8 *pos; struct sta_info *sta; @@ -88,10 +89,42 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, wnmtfs_ie = NULL; } + oci_ie = NULL; + oci_ie_len = 0; +#ifdef CONFIG_OCV + if (action_type == WNM_SLEEP_MODE_EXIT && + wpa_auth_uses_ocv(sta->wpa_sm)) { + struct wpa_channel_info ci; + + if (hostapd_drv_channel_info(hapd, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element in WNM-Sleep Mode frame"); + os_free(wnmtfs_ie); + return -1; + } + + oci_ie_len = OCV_OCI_EXTENDED_LEN; + oci_ie = os_zalloc(oci_ie_len); + if (!oci_ie) { + wpa_printf(MSG_WARNING, + "Failed to allocate buffer for OCI element in WNM-Sleep Mode frame"); + os_free(wnmtfs_ie); + return -1; + } + + if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { + os_free(wnmtfs_ie); + os_free(oci_ie); + return -1; + } + } +#endif /* CONFIG_OCV */ + #define MAX_GTK_SUBELEM_LEN 45 #define MAX_IGTK_SUBELEM_LEN 26 mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + - MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN); + MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN + + oci_ie_len); if (mgmt == NULL) { wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " "WNM-Sleep Response action frame"); @@ -134,11 +167,18 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len); /* copy TFS IE here */ pos += wnmsleep_ie_len; - if (wnmtfs_ie) + if (wnmtfs_ie) { os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len); + pos += wnmtfs_ie_len; + } +#ifdef CONFIG_OCV + /* copy OCV OCI here */ + if (oci_ie_len > 0) + os_memcpy(pos, oci_ie, oci_ie_len); +#endif /* CONFIG_OCV */ len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len + - igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len; + igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len; /* In driver, response frame should be forced to sent when STA is in * PS mode */ @@ -185,6 +225,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, #undef MAX_IGTK_SUBELEM_LEN fail: os_free(wnmtfs_ie); + os_free(oci_ie); os_free(mgmt); return res; } @@ -201,6 +242,11 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, u8 *tfsreq_ie_start = NULL; u8 *tfsreq_ie_end = NULL; u16 tfsreq_ie_len = 0; +#ifdef CONFIG_OCV + struct sta_info *sta; + const u8 *oci_ie = NULL; + u8 oci_ie_len = 0; +#endif /* CONFIG_OCV */ if (!hapd->conf->wnm_sleep_mode) { wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from " @@ -209,6 +255,13 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, return; } + if (len < 1) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore too short WNM-Sleep Mode Request from " + MACSTR, MAC2STR(addr)); + return; + } + dialog_token = *pos++; while (pos + 1 < frm + len) { u8 ie_len = pos[1]; @@ -221,6 +274,12 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, if (!tfsreq_ie_start) tfsreq_ie_start = (u8 *) pos; tfsreq_ie_end = (u8 *) pos; +#ifdef CONFIG_OCV + } else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 && + pos[2] == WLAN_EID_EXT_OCV_OCI) { + oci_ie = pos + 3; + oci_ie_len = ie_len - 1; +#endif /* CONFIG_OCV */ } else wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized", *pos); @@ -232,6 +291,27 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, return; } +#ifdef CONFIG_OCV + sta = ap_get_sta(hapd, addr); + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT && + sta && wpa_auth_uses_ocv(sta->wpa_sm)) { + struct wpa_channel_info ci; + + if (hostapd_drv_channel_info(hapd, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info to validate received OCI in WNM-Sleep Mode frame"); + return; + } + + if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx) != 0) { + wpa_msg(hapd, MSG_WARNING, "WNM: %s", ocv_errorstr); + return; + } + } +#endif /* CONFIG_OCV */ + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER && tfsreq_ie_start && tfsreq_ie_end && tfsreq_ie_end - tfsreq_ie_start >= 0) { diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 34969e79e46ff..f2e028c1599e8 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -1,6 +1,6 @@ /* * IEEE 802.11 RSN / WPA Authenticator - * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -13,6 +13,7 @@ #include "utils/state_machine.h" #include "utils/bitfield.h" #include "common/ieee802_11_defs.h" +#include "common/ocv.h" #include "crypto/aes.h" #include "crypto/aes_wrap.h" #include "crypto/aes_siv.h" @@ -22,6 +23,7 @@ #include "crypto/sha384.h" #include "crypto/random.h" #include "eapol_auth/eapol_auth_sm.h" +#include "drivers/driver.h" #include "ap_config.h" #include "ieee802_11.h" #include "wpa_auth.h" @@ -112,12 +114,13 @@ static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth, static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *p2p_dev_addr, - const u8 *prev_psk, size_t *psk_len) + const u8 *prev_psk, size_t *psk_len, + int *vlan_id) { if (wpa_auth->cb->get_psk == NULL) return NULL; return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr, - prev_psk, psk_len); + prev_psk, psk_len, vlan_id); } @@ -238,6 +241,26 @@ static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, } +#ifdef CONFIG_OCV +static int wpa_channel_info(struct wpa_authenticator *wpa_auth, + struct wpa_channel_info *ci) +{ + if (!wpa_auth->cb->channel_info) + return -1; + return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci); +} +#endif /* CONFIG_OCV */ + + +static int wpa_auth_update_vlan(struct wpa_authenticator *wpa_auth, + const u8 *addr, int vlan_id) +{ + if (!wpa_auth->cb->update_vlan) + return -1; + return wpa_auth->cb->update_vlan(wpa_auth->cb_ctx, addr, vlan_id); +} + + static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; @@ -297,6 +320,19 @@ static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx) } +void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm) +{ + if (sm && sm->wpa_auth->conf.wpa_ptk_rekey) { + wpa_printf(MSG_DEBUG, "WPA: Start PTK rekeying timer for " + MACSTR " (%d seconds)", MAC2STR(sm->addr), + sm->wpa_auth->conf.wpa_ptk_rekey); + eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); + eloop_register_timeout(sm->wpa_auth->conf.wpa_ptk_rekey, 0, + wpa_rekey_ptk, sm->wpa_auth, sm); + } +} + + static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx) { if (sm->pmksa == ctx) @@ -332,6 +368,10 @@ static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth, wpa_get_ntp_timestamp(buf + ETH_ALEN); ptr = (unsigned long) group; os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr)); +#ifdef TEST_FUZZ + os_memset(buf + ETH_ALEN, 0xab, 8); + os_memset(buf + ETH_ALEN + 8, 0xcd, sizeof(ptr)); +#endif /* TEST_FUZZ */ if (random_get_bytes(rkey, sizeof(rkey)) < 0) return -1; @@ -669,7 +709,10 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm) os_free(sm->last_rx_eapol_key); os_free(sm->wpa_ie); wpa_group_put(sm->wpa_auth, sm->group); - os_free(sm); +#ifdef CONFIG_DPP2 + wpabuf_clear_free(sm->dpp_z); +#endif /* CONFIG_DPP2 */ + bin_clear_free(sm, sizeof(*sm)); } @@ -835,13 +878,15 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, int ok = 0; const u8 *pmk = NULL; size_t pmk_len; + int vlan_id = 0; os_memset(&PTK, 0, sizeof(PTK)); for (;;) { if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, - sm->p2p_dev_addr, pmk, &pmk_len); + sm->p2p_dev_addr, pmk, &pmk_len, + &vlan_id); if (pmk == NULL) break; #ifdef CONFIG_IEEE80211R_AP @@ -860,6 +905,10 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK, data, data_len) == 0) { + if (sm->PMK != pmk) { + os_memcpy(sm->PMK, pmk, pmk_len); + sm->pmk_len = pmk_len; + } ok = 1; break; } @@ -878,6 +927,11 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, wpa_printf(MSG_DEBUG, "WPA: Earlier SNonce resulted in matching MIC"); sm->alt_snonce_valid = 0; + + if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && + wpa_auth_update_vlan(sm->wpa_auth, sm->addr, vlan_id) < 0) + return -1; + os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN); os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); sm->PTK_valid = TRUE; @@ -1202,6 +1256,11 @@ continue_processing: wpa_try_alt_snonce(sm, data, data_len))) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key with invalid MIC"); +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore Key MIC failure for fuzz testing"); + goto continue_fuzz; +#endif /* TEST_FUZZ */ return; } #ifdef CONFIG_FILS @@ -1210,9 +1269,17 @@ continue_processing: &key_data_length) < 0) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key with invalid MIC"); +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore Key MIC failure for fuzz testing"); + goto continue_fuzz; +#endif /* TEST_FUZZ */ return; } #endif /* CONFIG_FILS */ +#ifdef TEST_FUZZ + continue_fuzz: +#endif /* TEST_FUZZ */ sm->MICVerified = TRUE; eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); sm->pending_1_of_4_timeout = 0; @@ -1317,6 +1384,9 @@ static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr, os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN); pos = data + ETH_ALEN + WPA_NONCE_LEN; wpa_get_ntp_timestamp(pos); +#ifdef TEST_FUZZ + os_memset(pos, 0xef, 8); +#endif /* TEST_FUZZ */ pos += 8; if (random_get_bytes(pos, gtk_len) < 0) ret = -1; @@ -1596,6 +1666,9 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, timeout_ms = eapol_key_timeout_no_retrans; if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC)) sm->pending_1_of_4_timeout = 1; +#ifdef TEST_FUZZ + timeout_ms = 1; +#endif /* TEST_FUZZ */ wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry " "counter %u)", timeout_ms, ctr); eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, @@ -1670,6 +1743,14 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) case WPA_DEAUTH: case WPA_DISASSOC: sm->DeauthenticationRequest = TRUE; +#ifdef CONFIG_IEEE80211R_AP + os_memset(sm->PMK, 0, sizeof(sm->PMK)); + sm->pmk_len = 0; + os_memset(sm->xxkey, 0, sizeof(sm->xxkey)); + sm->xxkey_len = 0; + os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1)); + sm->pmk_r1_len = 0; +#endif /* CONFIG_IEEE80211R_AP */ break; case WPA_REAUTH: case WPA_REAUTH_EAPOL: @@ -1710,6 +1791,7 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) /* Using FT protocol, not WPA auth state machine */ sm->ft_completed = 1; + wpa_auth_set_ptk_rekey_timer(sm); return 0; #else /* CONFIG_IEEE80211R_AP */ break; @@ -1986,7 +2068,7 @@ SM_STATE(WPA_PTK, INITPSK) SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL, - &psk_len); + &psk_len, NULL); if (psk) { os_memcpy(sm->PMK, psk, psk_len); sm->pmk_len = psk_len; @@ -2000,6 +2082,10 @@ SM_STATE(WPA_PTK, INITPSK) wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache"); os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len); sm->pmk_len = sm->pmksa->pmk_len; +#ifdef CONFIG_IEEE80211R_AP + os_memcpy(sm->xxkey, sm->pmksa->pmk, sm->pmksa->pmk_len); + sm->xxkey_len = sm->pmksa->pmk_len; +#endif /* CONFIG_IEEE80211R_AP */ } #endif /* CONFIG_SAE */ sm->req_replay_counter_used = 0; @@ -2060,6 +2146,29 @@ SM_STATE(WPA_PTK, PTKSTART) wpa_printf(MSG_DEBUG, "RSN: No KCK available to derive PMKID for message 1/4"); pmkid = NULL; +#ifdef CONFIG_FILS + } else if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + if (sm->pmkid_set) { + wpa_hexdump(MSG_DEBUG, + "RSN: Message 1/4 PMKID from FILS/ERP", + sm->pmkid, PMKID_LEN); + os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], + sm->pmkid, PMKID_LEN); + } else { + /* No PMKID available */ + wpa_printf(MSG_DEBUG, + "RSN: No FILS/ERP PMKID available for message 1/4"); + pmkid = NULL; + } +#endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R_AP + } else if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) && + sm->ft_completed) { + wpa_printf(MSG_DEBUG, + "FT: No PMKID in message 1/4 when using FT protocol"); + pmkid = NULL; + pmkid_len = 0; +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE } else if (wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { if (sm->pmkid_set) { @@ -2098,14 +2207,36 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, const u8 *pmk, unsigned int pmk_len, struct wpa_ptk *ptk) { + const u8 *z = NULL; + size_t z_len = 0; + #ifdef CONFIG_IEEE80211R_AP - if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) - return wpa_auth_derive_ptk_ft(sm, pmk, ptk); + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + if (sm->ft_completed) { + u8 ptk_name[WPA_PMK_NAME_LEN]; + + return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, + sm->SNonce, sm->ANonce, + sm->addr, sm->wpa_auth->addr, + sm->pmk_r1_name, + ptk, ptk_name, + sm->wpa_key_mgmt, + sm->pairwise); + } + return wpa_auth_derive_ptk_ft(sm, ptk); + } #endif /* CONFIG_IEEE80211R_AP */ +#ifdef CONFIG_DPP2 + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) { + z = wpabuf_head(sm->dpp_z); + z_len = wpabuf_len(sm->dpp_z); + } +#endif /* CONFIG_DPP2 */ + return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion", sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce, - ptk, sm->wpa_key_mgmt, sm->pairwise); + ptk, sm->wpa_key_mgmt, sm->pairwise, z, z_len); } @@ -2155,6 +2286,16 @@ int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk, pmk_r0_name, WPA_PMK_NAME_LEN); wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name); os_memset(fils_ft, 0, sizeof(fils_ft)); + + res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder, + sm->addr, sm->pmk_r1_name, + use_sha384); + os_memset(pmk_r0, 0, PMK_LEN_MAX); + if (res < 0) + return -1; + wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name, + WPA_PMK_NAME_LEN); + sm->pmk_r1_name_valid = 1; } #endif /* CONFIG_IEEE80211R_AP */ @@ -2559,6 +2700,27 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm, wpabuf_put(plain, tmp2 - tmp); *len = (u8 *) wpabuf_put(plain, 0) - len - 1; + +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(sm)) { + struct wpa_channel_info ci; + u8 *pos; + + if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { + wpa_printf(MSG_WARNING, + "FILS: Failed to get channel info for OCI element"); + wpabuf_free(plain); + return NULL; + } + + pos = wpabuf_put(plain, OCV_OCI_EXTENDED_LEN); + if (ocv_insert_extended_oci(&ci, pos) < 0) { + wpabuf_free(plain); + return NULL; + } + } +#endif /* CONFIG_OCV */ + return plain; } @@ -2624,6 +2786,21 @@ u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *buf, #endif /* CONFIG_FILS */ +#ifdef CONFIG_OCV +int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth, + int ap_seg1_idx, int *bandwidth, int *seg1_idx) +{ + struct wpa_authenticator *wpa_auth = sm->wpa_auth; + + if (!wpa_auth->cb->get_sta_tx_params) + return -1; + return wpa_auth->cb->get_sta_tx_params(wpa_auth->cb_ctx, sm->addr, + ap_max_chanwidth, ap_seg1_idx, + bandwidth, seg1_idx); +} +#endif /* CONFIG_OCV */ + + SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) { struct wpa_authenticator *wpa_auth = sm->wpa_auth; @@ -2638,6 +2815,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; struct wpa_eapol_ie_parse kde; + int vlan_id = 0; SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); sm->EAPOLKeyReceived = FALSE; @@ -2653,7 +2831,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, - sm->p2p_dev_addr, pmk, &pmk_len); + sm->p2p_dev_addr, pmk, &pmk_len, + &vlan_id); if (pmk == NULL) break; psk_found = 1; @@ -2668,6 +2847,12 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) pmk_len = sm->pmk_len; } + if ((!pmk || !pmk_len) && sm->pmksa) { + wpa_printf(MSG_DEBUG, "WPA: Use PMK from PMKSA cache"); + pmk = sm->pmksa->pmk; + pmk_len = sm->pmksa->pmk_len; + } + if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK) < 0) break; @@ -2675,6 +2860,10 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK, sm->last_rx_eapol_key, sm->last_rx_eapol_key_len) == 0) { + if (sm->PMK != pmk) { + os_memcpy(sm->PMK, pmk, pmk_len); + sm->pmk_len = pmk_len; + } ok = 1; break; } @@ -2746,6 +2935,32 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) WLAN_REASON_PREV_AUTH_NOT_VALID); return; } +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(sm)) { + struct wpa_channel_info ci; + int tx_chanwidth; + int tx_seg1_idx; + + if (wpa_channel_info(wpa_auth, &ci) != 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "Failed to get channel info to validate received OCI in EAPOL-Key 2/4"); + return; + } + + if (get_sta_tx_parameters(sm, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx, &tx_chanwidth, + &tx_seg1_idx) < 0) + return; + + if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci, + tx_chanwidth, tx_seg1_idx) != 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + ocv_errorstr); + return; + } + } +#endif /* CONFIG_OCV */ #ifdef CONFIG_IEEE80211R_AP if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) { wpa_sta_disconnect(wpa_auth, sm->addr, @@ -2794,6 +3009,13 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) } #endif /* CONFIG_IEEE80211R_AP */ + if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && + wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) { + wpa_sta_disconnect(wpa_auth, sm->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + return; + } + sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); @@ -2883,6 +3105,36 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) #endif /* CONFIG_IEEE80211W */ +static int ocv_oci_len(struct wpa_state_machine *sm) +{ +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(sm)) + return OCV_OCI_KDE_LEN; +#endif /* CONFIG_OCV */ + return 0; +} + +static int ocv_oci_add(struct wpa_state_machine *sm, u8 **argpos) +{ +#ifdef CONFIG_OCV + struct wpa_channel_info ci; + + if (!wpa_auth_uses_ocv(sm)) + return 0; + + if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element"); + return -1; + } + + return ocv_insert_oci_kde(&ci, argpos); +#else /* CONFIG_OCV */ + return 0; +#endif /* CONFIG_OCV */ +} + + SM_STATE(WPA_PTK, PTKINITNEGOTIATING) { u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32]; @@ -2966,7 +3218,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) } } - kde_len = wpa_ie_len + ieee80211w_kde_len(sm); + kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm); if (gtk) kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; #ifdef CONFIG_IEEE80211R_AP @@ -3011,6 +3263,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) gtk, gtk_len); } pos = ieee80211w_kde_add(sm, pos); + if (ocv_oci_add(sm, &pos) < 0) { + os_free(kde); + return; + } #ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { @@ -3094,12 +3350,7 @@ SM_STATE(WPA_PTK, PTKINITDONE) /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ sm->pairwise_set = TRUE; - if (sm->wpa_auth->conf.wpa_ptk_rekey) { - eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); - eloop_register_timeout(sm->wpa_auth->conf. - wpa_ptk_rekey, 0, wpa_rekey_ptk, - sm->wpa_auth, sm); - } + wpa_auth_set_ptk_rekey_timer(sm); if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP || @@ -3199,7 +3450,7 @@ SM_STEP(WPA_PTK) break; case WPA_PTK_INITPSK: if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, - NULL, NULL)) { + NULL, NULL, NULL)) { SM_ENTER(WPA_PTK, PTKSTART); #ifdef CONFIG_SAE } else if (wpa_auth_uses_sae(sm) && sm->pmksa) { @@ -3322,7 +3573,7 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) } if (sm->wpa == WPA_VERSION_WPA2) { kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + - ieee80211w_kde_len(sm); + ieee80211w_kde_len(sm) + ocv_oci_len(sm); kde_buf = os_malloc(kde_len); if (kde_buf == NULL) return; @@ -3333,6 +3584,10 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, gtk, gsm->GTK_len); pos = ieee80211w_kde_add(sm, pos); + if (ocv_oci_add(sm, &pos) < 0) { + os_free(kde_buf); + return; + } kde_len = pos - kde; } else { kde = gtk; @@ -3353,8 +3608,67 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) { +#ifdef CONFIG_OCV + struct wpa_authenticator *wpa_auth = sm->wpa_auth; + const u8 *key_data, *mic; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + struct wpa_eapol_ie_parse kde; + size_t mic_len; + u16 key_data_length; +#endif /* CONFIG_OCV */ + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); sm->EAPOLKeyReceived = FALSE; + +#ifdef CONFIG_OCV + mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); + + /* + * Note: last_rx_eapol_key length fields have already been validated in + * wpa_receive(). + */ + hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key; + key = (struct wpa_eapol_key *) (hdr + 1); + mic = (u8 *) (key + 1); + key_data = mic + mic_len + 2; + key_data_length = WPA_GET_BE16(mic + mic_len); + if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) - + sizeof(*key) - mic_len - 2) + return; + + if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key group msg 2/2 with invalid Key Data contents"); + return; + } + + if (wpa_auth_uses_ocv(sm)) { + struct wpa_channel_info ci; + int tx_chanwidth; + int tx_seg1_idx; + + if (wpa_channel_info(wpa_auth, &ci) != 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "Failed to get channel info to validate received OCI in EAPOL-Key group 1/2"); + return; + } + + if (get_sta_tx_parameters(sm, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx, &tx_chanwidth, + &tx_seg1_idx) < 0) + return; + + if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci, + tx_chanwidth, tx_seg1_idx) != 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + ocv_errorstr); + return; + } + } +#endif /* CONFIG_OCV */ + if (sm->GUpdateStationKeys) sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; @@ -3963,6 +4277,15 @@ int wpa_auth_get_pairwise(struct wpa_state_machine *sm) } +const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len) +{ + if (!sm) + return NULL; + *len = sm->pmk_len; + return sm->PMK; +} + + int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm) { if (sm == NULL) @@ -4575,9 +4898,37 @@ void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm, *fils_kek_len = sm->PTK.kek_len; } + +void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk, + size_t pmk_len, const u8 *pmkid) +{ + os_memcpy(sm->PMK, pmk, pmk_len); + sm->pmk_len = pmk_len; + os_memcpy(sm->pmkid, pmkid, PMKID_LEN); + sm->pmkid_set = 1; +} + #endif /* CONFIG_FILS */ +void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg) +{ + if (sm) + sm->auth_alg = auth_alg; +} + + +#ifdef CONFIG_DPP2 +void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z) +{ + if (sm) { + wpabuf_clear_free(sm->dpp_z); + sm->dpp_z = z ? wpabuf_dup(z) : NULL; + } +} +#endif /* CONFIG_DPP2 */ + + #ifdef CONFIG_TESTING_OPTIONS int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce, @@ -4666,7 +5017,7 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm, } } - kde_len = wpa_ie_len + ieee80211w_kde_len(sm); + kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm); if (gtk) kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; #ifdef CONFIG_IEEE80211R_AP @@ -4715,6 +5066,10 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm, os_memset(opos, 0, 6); /* clear PN */ } #endif /* CONFIG_IEEE80211W */ + if (ocv_oci_add(sm, &pos) < 0) { + os_free(kde); + return -1; + } #ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { @@ -4796,7 +5151,7 @@ int wpa_auth_resend_group_m1(struct wpa_state_machine *sm, gtk = gsm->GTK[gsm->GN - 1]; if (sm->wpa == WPA_VERSION_WPA2) { kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + - ieee80211w_kde_len(sm); + ieee80211w_kde_len(sm) + ocv_oci_len(sm); kde_buf = os_malloc(kde_len); if (kde_buf == NULL) return -1; @@ -4816,6 +5171,10 @@ int wpa_auth_resend_group_m1(struct wpa_state_machine *sm, os_memset(opos, 0, 6); /* clear PN */ } #endif /* CONFIG_IEEE80211W */ + if (ocv_oci_add(sm, &pos) < 0) { + os_free(kde_buf); + return -1; + } kde_len = pos - kde; } else { kde = gtk; diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index fad5536f740e9..df1e17a003f8c 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -145,6 +145,7 @@ struct wpa_state_machine; struct rsn_pmksa_cache_entry; struct eapol_state_machine; struct ft_remote_seq; +struct wpa_channel_info; struct ft_remote_r0kh { @@ -191,6 +192,9 @@ struct wpa_auth_config { int group_mgmt_cipher; int sae_require_mfp; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + int ocv; /* Operating Channel Validation */ +#endif /* CONFIG_OCV */ #ifdef CONFIG_IEEE80211R_AP u8 ssid[SSID_MAX_LEN]; size_t ssid_len; @@ -250,7 +254,8 @@ struct wpa_auth_callbacks { int value); int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, - const u8 *prev_psk, size_t *psk_len); + const u8 *prev_psk, size_t *psk_len, + int *vlan_id); int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len); int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len); @@ -265,6 +270,11 @@ struct wpa_auth_callbacks { size_t data_len); int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data, size_t data_len); + int (*channel_info)(void *ctx, struct wpa_channel_info *ci); + int (*update_vlan)(void *ctx, const u8 *addr, int vlan_id); + int (*get_sta_tx_params)(void *ctx, const u8 *addr, + int ap_max_chanwidth, int ap_seg1_idx, + int *bandwidth, int *seg1_idx); #ifdef CONFIG_IEEE80211R_AP struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); int (*set_vlan)(void *ctx, const u8 *sta_addr, @@ -308,7 +318,7 @@ enum { }; int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, + struct wpa_state_machine *sm, int freq, const u8 *wpa_ie, size_t wpa_ie_len, const u8 *mdie, size_t mdie_len, const u8 *owe_dh, size_t owe_dh_len); @@ -316,6 +326,8 @@ int wpa_validate_osen(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, const u8 *osen_ie, size_t osen_ie_len); int wpa_auth_uses_mfp(struct wpa_state_machine *sm); +void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv); +int wpa_auth_uses_ocv(struct wpa_state_machine *sm); struct wpa_state_machine * wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *p2p_dev_addr); @@ -339,6 +351,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen); void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth); int wpa_auth_pairwise_set(struct wpa_state_machine *sm); int wpa_auth_get_pairwise(struct wpa_state_machine *sm); +const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len); int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm); int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm); int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm); @@ -449,14 +462,21 @@ const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm, int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len); +int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth, + int ap_seg1_idx, int *bandwidth, int *seg1_idx); + int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384, u8 *buf, size_t len); void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm, u8 *fils_anonce, u8 *fils_snonce, u8 *fils_kek, size_t *fils_kek_len); +void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk, + size_t pmk_len, const u8 *pmkid); u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, u8 *pos, size_t max_len, const u8 *req_ies, size_t req_ies_len); +void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg); +void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z); int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce, void (*cb)(void *ctx1, void *ctx2), @@ -468,5 +488,6 @@ int wpa_auth_resend_group_m1(struct wpa_state_machine *sm, void (*cb)(void *ctx1, void *ctx2), void *ctx1, void *ctx2); int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth); +void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm); #endif /* WPA_AUTH_H */ diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index f6792e00f32d8..ac16199a6006f 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -13,6 +13,8 @@ #include "utils/list.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "common/ocv.h" +#include "drivers/driver.h" #include "crypto/aes.h" #include "crypto/aes_siv.h" #include "crypto/aes_wrap.h" @@ -64,7 +66,7 @@ struct tlv_list { * Returns: 0 on success, -1 on error */ static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len, - const u8 *enc, const size_t enc_len, + const u8 *enc, size_t enc_len, const u8 *auth, const size_t auth_len, const u8 *src_addr, u8 type, u8 **plain, size_t *plain_size) @@ -72,7 +74,11 @@ static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len, const u8 *ad[3] = { src_addr, auth, &type }; size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) }; + wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u", + MAC2STR(src_addr), type); wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypt using key", key, key_len); + wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs", enc, enc_len); + wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len); if (!key) { /* skip decryption */ *plain = os_memdup(enc, enc_len); @@ -95,8 +101,18 @@ static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len, goto err; if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len, - *plain) < 0) - goto err; + *plain) < 0) { + if (enc_len < AES_BLOCK_SIZE + 2) + goto err; + + /* Try to work around Ethernet devices that add extra + * two octet padding even if the frame is longer than + * the minimum Ethernet frame. */ + enc_len -= 2; + if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len, + *plain) < 0) + goto err; + } *plain_size = enc_len - AES_BLOCK_SIZE; wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypted TLVs", @@ -461,9 +477,12 @@ static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len, const u8 *ad[3] = { src_addr, auth, &type }; size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) }; + wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u", + MAC2STR(src_addr), type); wpa_hexdump_key(MSG_DEBUG, "FT(RRB): plaintext message", plain, plain_len); wpa_hexdump_key(MSG_DEBUG, "FT(RRB): encrypt using key", key, key_len); + wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len); if (!key) { /* encryption not needed, return plaintext as packet */ @@ -473,6 +492,8 @@ static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len, wpa_printf(MSG_ERROR, "FT: Failed to encrypt RRB-OUI message"); return -1; } + wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs", + enc, plain_len + AES_BLOCK_SIZE); return 0; } @@ -501,9 +522,10 @@ static int wpa_ft_rrb_build(const u8 *key, const size_t key_len, const u8 *src_addr, u8 type, u8 **packet, size_t *packet_len) { - u8 *plain = NULL, *auth = NULL, *pos; + u8 *plain = NULL, *auth = NULL, *pos, *tmp; size_t plain_len = 0, auth_len = 0; int ret = -1; + size_t pad_len = 0; *packet = NULL; if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, vlan, &plain, &plain_len) < 0) @@ -515,6 +537,28 @@ static int wpa_ft_rrb_build(const u8 *key, const size_t key_len, *packet_len = sizeof(u16) + auth_len + plain_len; if (key) *packet_len += AES_BLOCK_SIZE; +#define RRB_MIN_MSG_LEN 64 + if (*packet_len < RRB_MIN_MSG_LEN) { + pad_len = RRB_MIN_MSG_LEN - *packet_len; + if (pad_len < sizeof(struct ft_rrb_tlv)) + pad_len = sizeof(struct ft_rrb_tlv); + wpa_printf(MSG_DEBUG, + "FT: Pad message to minimum Ethernet frame length (%d --> %d)", + (int) *packet_len, (int) (*packet_len + pad_len)); + *packet_len += pad_len; + tmp = os_realloc(auth, auth_len + pad_len); + if (!tmp) + goto out; + auth = tmp; + pos = auth + auth_len; + WPA_PUT_LE16(pos, FT_RRB_LAST_EMPTY); + pos += 2; + WPA_PUT_LE16(pos, pad_len - sizeof(struct ft_rrb_tlv)); + pos += 2; + os_memset(pos, 0, pad_len - sizeof(struct ft_rrb_tlv)); + auth_len += pad_len; + + } *packet = os_zalloc(*packet_len); if (!*packet) goto out; @@ -527,6 +571,7 @@ static int wpa_ft_rrb_build(const u8 *key, const size_t key_len, if (wpa_ft_rrb_encrypt(key, key_len, plain, plain_len, auth, auth_len, src_addr, type, pos) < 0) goto out; + wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", *packet, *packet_len); ret = 0; @@ -594,8 +639,8 @@ static int wpa_ft_rrb_oui_send(struct wpa_authenticator *wpa_auth, { if (!wpa_auth->cb->send_oui) return -1; - wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR, - oui_suffix, MAC2STR(dst)); + wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR " (len=%u)", + oui_suffix, MAC2STR(dst), (unsigned int) data_len); return wpa_auth->cb->send_oui(wpa_auth->cb_ctx, dst, oui_suffix, data, data_len); } @@ -618,7 +663,7 @@ static const u8 * wpa_ft_get_psk(struct wpa_authenticator *wpa_auth, if (wpa_auth->cb->get_psk == NULL) return NULL; return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr, - prev_psk, NULL); + prev_psk, NULL, NULL); } @@ -727,6 +772,17 @@ static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth, } +#ifdef CONFIG_OCV +static int wpa_channel_info(struct wpa_authenticator *wpa_auth, + struct wpa_channel_info *ci) +{ + if (!wpa_auth->cb->channel_info) + return -1; + return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci); +} +#endif /* CONFIG_OCV */ + + int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) { u8 *pos = buf; @@ -894,6 +950,8 @@ wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth, goto err; } + wpa_printf(MSG_DEBUG, "FT: Send out sequence number request to " MACSTR, + MAC2STR(src_addr)); item = os_zalloc(sizeof(*item)); if (!item) goto err; @@ -2016,8 +2074,7 @@ int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, } -int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, - struct wpa_ptk *ptk) +int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk) { u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN]; size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ? @@ -2373,10 +2430,24 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, end = pos + max_len; - if (auth_alg == WLAN_AUTH_FT) { + if (auth_alg == WLAN_AUTH_FT || + ((auth_alg == WLAN_AUTH_FILS_SK || + auth_alg == WLAN_AUTH_FILS_SK_PFS || + auth_alg == WLAN_AUTH_FILS_PK) && + (sm->wpa_key_mgmt & (WPA_KEY_MGMT_FT_FILS_SHA256 | + WPA_KEY_MGMT_FT_FILS_SHA384)))) { + if (!sm->pmk_r1_name_valid) { + wpa_printf(MSG_ERROR, + "FT: PMKR1Name is not valid for Assoc Resp RSNE"); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name for Assoc Resp RSNE", + sm->pmk_r1_name, WPA_PMK_NAME_LEN); /* * RSN (only present if this is a Reassociation Response and - * part of a fast BSS transition) + * part of a fast BSS transition; or if this is a + * (Re)Association Response frame during an FT initial mobility + * domain association using FILS) */ res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name); if (res < 0) @@ -2430,6 +2501,35 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, os_free(igtk); } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(sm)) { + struct wpa_channel_info ci; + u8 *nbuf, *ocipos; + + if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element"); + os_free(subelem); + return NULL; + } + + subelem_len += 2 + OCV_OCI_LEN; + nbuf = os_realloc(subelem, subelem_len); + if (!nbuf) { + os_free(subelem); + return NULL; + } + subelem = nbuf; + + ocipos = subelem + subelem_len - 2 - OCV_OCI_LEN; + *ocipos++ = FTIE_SUBELEM_OCI; + *ocipos++ = OCV_OCI_LEN; + if (ocv_insert_oci(&ci, &ocipos) < 0) { + os_free(subelem); + return NULL; + } + } +#endif /* CONFIG_OCV */ } else { r0kh_id = conf->r0_key_holder; r0kh_id_len = conf->r0_key_holder_len; @@ -2596,6 +2696,8 @@ static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm, os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN); if (out_pairwise) *out_pairwise = pairwise; + os_memcpy(sm->PMK, pmk, PMK_LEN); + sm->pmk_len = PMK_LEN; if (out_vlan && wpa_ft_get_vlan(sm->wpa_auth, sm->addr, out_vlan) < 0) { wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " @@ -2881,6 +2983,8 @@ pmk_r1_derived: wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len); sm->pmk_r1_name_valid = 1; os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); + os_memcpy(sm->pmk_r1, pmk_r1, pmk_r1_len); + sm->pmk_r1_len = pmk_r1_len; if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " @@ -3178,6 +3282,32 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, return WLAN_STATUS_INVALID_FTIE; } +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(sm)) { + struct wpa_channel_info ci; + int tx_chanwidth; + int tx_seg1_idx; + + if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info to validate received OCI in (Re)Assoc Request"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (get_sta_tx_parameters(sm, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx, &tx_chanwidth, + &tx_seg1_idx) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + if (ocv_verify_tx_params(parse.oci, parse.oci_len, &ci, + tx_chanwidth, tx_seg1_idx) != 0) { + wpa_printf(MSG_WARNING, "%s", ocv_errorstr); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + } +#endif /* CONFIG_OCV */ + return WLAN_STATUS_SUCCESS; } @@ -4303,6 +4433,7 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, wpa_printf(MSG_DEBUG, "FT: RRB-OUI received frame from remote AP " MACSTR, MAC2STR(src_addr)); wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame - oui_suffix=%d", oui_suffix); + wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", data, data_len); if (is_multicast_ether_addr(src_addr)) { wpa_printf(MSG_DEBUG, @@ -4331,8 +4462,10 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, } auth = data + sizeof(u16); + wpa_hexdump(MSG_MSGDUMP, "FT: Authenticated payload", auth, alen); enc = data + sizeof(u16) + alen; elen = data_len - sizeof(u16) - alen; + wpa_hexdump(MSG_MSGDUMP, "FT: Encrypted payload", enc, elen); switch (oui_suffix) { case FT_PACKET_R0KH_R1KH_PULL: diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 812740301c8b2..45172c69a9fad 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -27,6 +27,7 @@ #include "tkip_countermeasures.h" #include "ap_drv_ops.h" #include "ap_config.h" +#include "ieee802_11.h" #include "pmksa_cache_auth.h" #include "wpa_auth.h" #include "wpa_auth_glue.h" @@ -55,6 +56,9 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->wmm_enabled = conf->wmm_enabled; wconf->wmm_uapsd = conf->wmm_uapsd; wconf->disable_pmksa_caching = conf->disable_pmksa_caching; +#ifdef CONFIG_OCV + wconf->ocv = conf->ocv; +#endif /* CONFIG_OCV */ wconf->okc = conf->okc; #ifdef CONFIG_IEEE80211W wconf->ieee80211w = conf->ieee80211w; @@ -242,12 +246,15 @@ static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr, static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, - const u8 *prev_psk, size_t *psk_len) + const u8 *prev_psk, size_t *psk_len, + int *vlan_id) { struct hostapd_data *hapd = ctx; struct sta_info *sta = ap_get_sta(hapd, addr); const u8 *psk; + if (vlan_id) + *vlan_id = 0; if (psk_len) *psk_len = PMK_LEN; @@ -283,7 +290,8 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, } #endif /* CONFIG_OWE */ - psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk); + psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk, + vlan_id); /* * This is about to iterate over all psks, prev_psk gives the last * returned psk which should not be returned again. @@ -291,6 +299,9 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, */ if (sta && sta->psk && !psk) { struct hostapd_sta_wpa_psk_short *pos; + + if (vlan_id) + *vlan_id = 0; psk = sta->psk->psk; for (pos = sta->psk; pos; pos = pos->next) { if (pos->is_passphrase) { @@ -776,6 +787,74 @@ static int hostapd_wpa_auth_send_oui(void *ctx, const u8 *dst, u8 oui_suffix, } +static int hostapd_channel_info(void *ctx, struct wpa_channel_info *ci) +{ + struct hostapd_data *hapd = ctx; + + return hostapd_drv_channel_info(hapd, ci); +} + + +static int hostapd_wpa_auth_update_vlan(void *ctx, const u8 *addr, int vlan_id) +{ +#ifndef CONFIG_NO_VLAN + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + struct vlan_description vlan_desc; + + sta = ap_get_sta(hapd, addr); + if (!sta) + return -1; + + os_memset(&vlan_desc, 0, sizeof(vlan_desc)); + vlan_desc.notempty = 1; + vlan_desc.untagged = vlan_id; + if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) { + wpa_printf(MSG_INFO, "Invalid VLAN ID %d in wpa_psk_file", + vlan_id); + return -1; + } + + if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0) { + wpa_printf(MSG_INFO, + "Failed to assign VLAN ID %d from wpa_psk_file to " + MACSTR, vlan_id, MAC2STR(sta->addr)); + return -1; + } + + wpa_printf(MSG_INFO, + "Assigned VLAN ID %d from wpa_psk_file to " MACSTR, + vlan_id, MAC2STR(sta->addr)); + if ((sta->flags & WLAN_STA_ASSOC) && + ap_sta_bind_vlan(hapd, sta) < 0) + return -1; +#endif /* CONFIG_NO_VLAN */ + + return 0; +} + + +#ifdef CONFIG_OCV +static int hostapd_get_sta_tx_params(void *ctx, const u8 *addr, + int ap_max_chanwidth, int ap_seg1_idx, + int *bandwidth, int *seg1_idx) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (!sta) { + hostapd_wpa_auth_logger(hapd, addr, LOGGER_INFO, + "Failed to get STA info to validate received OCI"); + return -1; + } + + return get_tx_parameters(sta, ap_max_chanwidth, ap_seg1_idx, bandwidth, + seg1_idx); +} +#endif /* CONFIG_OCV */ + + #ifdef CONFIG_IEEE80211R_AP static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst, @@ -814,12 +893,18 @@ hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr) struct hostapd_data *hapd = ctx; struct sta_info *sta; + wpa_printf(MSG_DEBUG, "Add station entry for " MACSTR + " based on WPA authenticator callback", + MAC2STR(sta_addr)); if (hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT) < 0) return NULL; sta = ap_sta_add(hapd, sta_addr); if (sta == NULL) return NULL; + if (hapd->driver && hapd->driver->add_sta_node) + sta->added_unassoc = 1; + sta->ft_over_ds = 1; if (sta->wpa_sm) { sta->auth_alg = WLAN_AUTH_FT; return sta->wpa_sm; @@ -1189,6 +1274,11 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) .for_each_auth = hostapd_wpa_auth_for_each_auth, .send_ether = hostapd_wpa_auth_send_ether, .send_oui = hostapd_wpa_auth_send_oui, + .channel_info = hostapd_channel_info, + .update_vlan = hostapd_wpa_auth_update_vlan, +#ifdef CONFIG_OCV + .get_sta_tx_params = hostapd_get_sta_tx_params, +#endif /* CONFIG_OCV */ #ifdef CONFIG_IEEE80211R_AP .send_ft_action = hostapd_wpa_auth_send_ft_action, .add_sta = hostapd_wpa_auth_add_sta, diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index b1cea1b49b14c..4babd0cbb044c 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -22,6 +22,7 @@ struct wpa_state_machine { u8 addr[ETH_ALEN]; u8 p2p_dev_addr[ETH_ALEN]; + u16 auth_alg; enum { WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, @@ -92,6 +93,9 @@ struct wpa_state_machine { #endif /* CONFIG_IEEE80211R_AP */ unsigned int is_wnmsleep:1; unsigned int pmkid_set:1; +#ifdef CONFIG_OCV + unsigned int ocv_enabled:1; +#endif /* CONFIG_OCV */ u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; int req_replay_counter_used; @@ -115,6 +119,8 @@ struct wpa_state_machine { u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the * first 384 bits of MSK */ size_t xxkey_len; + u8 pmk_r1[PMK_LEN_MAX]; + unsigned int pmk_r1_len; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth * Request */ u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */ @@ -147,6 +153,10 @@ struct wpa_state_machine { unsigned int fils_completed:1; #endif /* CONFIG_FILS */ +#ifdef CONFIG_DPP2 + struct wpabuf *dpp_z; +#endif /* CONFIG_DPP2 */ + #ifdef CONFIG_TESTING_OPTIONS void (*eapol_status_cb)(void *ctx1, void *ctx2); void *eapol_status_cb_ctx1; @@ -282,8 +292,7 @@ int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384, const u8 *anonce, const u8 *snonce, u8 *buf, size_t len, const u8 *subelem, size_t subelem_len); -int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, - struct wpa_ptk *ptk); +int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk); struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void); void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache); void wpa_ft_install_ptk(struct wpa_state_machine *sm); diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index cdcc5de39d457..8580a5a69be8a 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -293,9 +293,13 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, capab |= WPA_CAPABILITY_MFPR; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + if (conf->ocv) + capab |= WPA_CAPABILITY_OCVC; +#endif /* CONFIG_OCV */ #ifdef CONFIG_RSN_TESTING if (rsn_testing) - capab |= BIT(8) | BIT(14) | BIT(15); + capab |= BIT(8) | BIT(15); #endif /* CONFIG_RSN_TESTING */ WPA_PUT_LE16(pos, capab); pos += 2; @@ -414,6 +418,10 @@ static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid) capab |= WPA_CAPABILITY_MFPR; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + if (conf->ocv) + capab |= WPA_CAPABILITY_OCVC; +#endif /* CONFIG_OCV */ WPA_PUT_LE16(eid, capab); eid += 2; @@ -522,7 +530,7 @@ static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx) int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, + struct wpa_state_machine *sm, int freq, const u8 *wpa_ie, size_t wpa_ie_len, const u8 *mdie, size_t mdie_len, const u8 *owe_dh, size_t owe_dh_len) @@ -552,6 +560,23 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, if (version == WPA_PROTO_RSN) { res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data); + if (!data.has_pairwise) + data.pairwise_cipher = wpa_default_rsn_cipher(freq); + if (!data.has_group) + data.group_cipher = wpa_default_rsn_cipher(freq); + + if (wpa_key_mgmt_ft(data.key_mgmt) && !mdie && + !wpa_key_mgmt_only_ft(data.key_mgmt)) { + /* Workaround for some HP and Epson printers that seem + * to incorrectly copy the FT-PSK + WPA-PSK AKMs from AP + * advertised RSNE to Association Request frame. */ + wpa_printf(MSG_DEBUG, + "RSN: FT set in RSNE AKM but MDE is missing from " + MACSTR + " - ignore FT AKM(s) because there's also a non-FT AKM", + MAC2STR(sm->addr)); + data.key_mgmt &= ~WPA_KEY_MGMT_FT; + } selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; if (0) { @@ -760,6 +785,17 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, } #endif /* CONFIG_SAE */ +#ifdef CONFIG_OCV + if ((data.capabilities & WPA_CAPABILITY_OCVC) && + !(data.capabilities & WPA_CAPABILITY_MFPC)) { + wpa_printf(MSG_DEBUG, + "Management frame protection required with OCV, but client did not enable it"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + wpa_auth_set_ocv(sm, wpa_auth->conf.ocv && + (data.capabilities & WPA_CAPABILITY_OCVC)); +#endif /* CONFIG_OCV */ + if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION || !(data.capabilities & WPA_CAPABILITY_MFPC)) sm->mgmt_frame_prot = 0; @@ -799,6 +835,12 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, "OWE: No Diffie-Hellman Parameter element"); return WPA_INVALID_AKMP; } +#ifdef CONFIG_DPP + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && owe_dh) { + /* Diffie-Hellman Parameter element can be used with DPP as + * well, so allow this to proceed. */ + } else +#endif /* CONFIG_DPP */ if (sm->wpa_key_mgmt != WPA_KEY_MGMT_OWE && owe_dh) { wpa_printf(MSG_DEBUG, "OWE: Unexpected Diffie-Hellman Parameter element with non-OWE AKM"); @@ -816,6 +858,21 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, else sm->wpa = WPA_VERSION_WPA; +#if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS) + if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 || + sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) && + (sm->auth_alg == WLAN_AUTH_FILS_SK || + sm->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sm->auth_alg == WLAN_AUTH_FILS_PK) && + (data.num_pmkid != 1 || !data.pmkid || !sm->pmk_r1_name_valid || + os_memcmp_const(data.pmkid, sm->pmk_r1_name, + WPA_PMK_NAME_LEN) != 0)) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "No PMKR1Name match for FILS+FT"); + return WPA_INVALID_PMKID; + } +#endif /* CONFIG_IEEE80211R_AP && CONFIG_FILS */ + sm->pmksa = NULL; for (i = 0; i < data.num_pmkid; i++) { wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID", @@ -995,6 +1052,15 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, } #endif /* CONFIG_P2P */ +#ifdef CONFIG_OCV + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_OCI) { + ie->oci = pos + 2 + RSN_SELECTOR_LEN; + ie->oci_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } +#endif /* CONFIG_OCV */ + return 0; } @@ -1062,6 +1128,23 @@ int wpa_auth_uses_mfp(struct wpa_state_machine *sm) } +#ifdef CONFIG_OCV + +void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv) +{ + if (sm) + sm->ocv_enabled = ocv; +} + + +int wpa_auth_uses_ocv(struct wpa_state_machine *sm) +{ + return sm ? sm->ocv_enabled : 0; +} + +#endif /* CONFIG_OCV */ + + #ifdef CONFIG_OWE u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, u8 *pos, size_t max_len, diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h index 73e433349049b..a38b206fd0f44 100644 --- a/src/ap/wpa_auth_ie.h +++ b/src/ap/wpa_auth_ie.h @@ -33,6 +33,10 @@ struct wpa_eapol_ie_parse { const u8 *ip_addr_req; const u8 *ip_addr_alloc; #endif /* CONFIG_P2P */ +#ifdef CONFIG_OCV + const u8 *oci; + size_t oci_len; +#endif /* CONFIG_OCV */ const u8 *osen; size_t osen_len; diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index 5ec019971f372..6161cdbdb922c 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -354,6 +354,18 @@ static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd, bss->wpa_pairwise, bss->rsn_pairwise); + if (hapd->conf->wps_cred_add_sae && + (cred->auth_type & WPS_AUTH_WPA2PSK) && + cred->key_len != 2 * PMK_LEN) { + bss->wpa_key_mgmt |= WPA_KEY_MGMT_SAE; +#ifdef CONFIG_IEEE80211W + if (bss->ieee80211w == NO_MGMT_FRAME_PROTECTION) + bss->ieee80211w = + MGMT_FRAME_PROTECTION_OPTIONAL; + bss->sae_require_mfp = 1; +#endif /* CONFIG_IEEE80211W */ + } + if (cred->key_len >= 8 && cred->key_len < 64) { os_free(bss->ssid.wpa_passphrase); bss->ssid.wpa_passphrase = os_zalloc(cred->key_len + 1); @@ -401,6 +413,7 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) char buf[1024]; int multi_bss; int wpa; + int pmf_changed = 0; if (hapd->wps == NULL) return 0; @@ -520,6 +533,10 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) if (wpa) { char *prefix; +#ifdef CONFIG_IEEE80211W + int sae = 0; +#endif /* CONFIG_IEEE80211W */ + fprintf(nconf, "wpa=%d\n", wpa); fprintf(nconf, "wpa_key_mgmt="); @@ -528,10 +545,30 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) fprintf(nconf, "WPA-EAP"); prefix = " "; } - if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) + if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) { fprintf(nconf, "%sWPA-PSK", prefix); + prefix = " "; + } + if (hapd->conf->wps_cred_add_sae && + (cred->auth_type & WPS_AUTH_WPA2PSK) && + cred->key_len != 2 * PMK_LEN) { + fprintf(nconf, "%sSAE", prefix); +#ifdef CONFIG_IEEE80211W + sae = 1; +#endif /* CONFIG_IEEE80211W */ + } fprintf(nconf, "\n"); +#ifdef CONFIG_IEEE80211W + if (sae && hapd->conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) { + fprintf(nconf, "ieee80211w=%d\n", + MGMT_FRAME_PROTECTION_OPTIONAL); + pmf_changed = 1; + } + if (sae) + fprintf(nconf, "sae_require_mfp=1\n"); +#endif /* CONFIG_IEEE80211W */ + fprintf(nconf, "wpa_pairwise="); prefix = ""; if (cred->encr_type & WPS_ENCR_AES) { @@ -585,6 +622,7 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) str_starts(buf, "wep_default_key=") || str_starts(buf, "wep_key") || str_starts(buf, "wps_state=") || + (pmf_changed && str_starts(buf, "ieee80211w=")) || str_starts(buf, "wpa=") || str_starts(buf, "wpa_psk=") || str_starts(buf, "wpa_pairwise=") || @@ -975,6 +1013,7 @@ int hostapd_init_wps(struct hostapd_data *hapd, { struct wps_context *wps; struct wps_registrar_config cfg; + u8 *multi_ap_netw_key = NULL; if (conf->wps_state == 0) { hostapd_wps_clear_ies(hapd, 0); @@ -1133,6 +1172,31 @@ int hostapd_init_wps(struct hostapd_data *hapd, wps->encr_types_wpa = WPS_ENCR_AES | WPS_ENCR_TKIP; } + if ((hapd->conf->multi_ap & FRONTHAUL_BSS) && + hapd->conf->multi_ap_backhaul_ssid.ssid_len) { + cfg.multi_ap_backhaul_ssid_len = + hapd->conf->multi_ap_backhaul_ssid.ssid_len; + cfg.multi_ap_backhaul_ssid = + hapd->conf->multi_ap_backhaul_ssid.ssid; + + if (conf->multi_ap_backhaul_ssid.wpa_passphrase) { + cfg.multi_ap_backhaul_network_key = (const u8 *) + conf->multi_ap_backhaul_ssid.wpa_passphrase; + cfg.multi_ap_backhaul_network_key_len = + os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase); + } else if (conf->multi_ap_backhaul_ssid.wpa_psk) { + multi_ap_netw_key = os_malloc(2 * PMK_LEN + 1); + if (!multi_ap_netw_key) + goto fail; + wpa_snprintf_hex((char *) multi_ap_netw_key, + 2 * PMK_LEN + 1, + conf->multi_ap_backhaul_ssid.wpa_psk->psk, + PMK_LEN); + cfg.multi_ap_backhaul_network_key = multi_ap_netw_key; + cfg.multi_ap_backhaul_network_key_len = 2 * PMK_LEN; + } + } + wps->ap_settings = conf->ap_settings; wps->ap_settings_len = conf->ap_settings_len; @@ -1174,10 +1238,12 @@ int hostapd_init_wps(struct hostapd_data *hapd, hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd); hapd->wps = wps; + bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN); return 0; fail: + bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN); hostapd_free_wps(wps); return -1; } diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c index 0b596bbcc6313..30c52476bbed8 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-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2014-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,10 +10,12 @@ #include "utils/common.h" #include "utils/module_tests.h" +#include "crypto/crypto.h" #include "ieee802_11_common.h" #include "ieee802_11_defs.h" #include "gas.h" #include "wpa_common.h" +#include "sae.h" struct ieee802_11_parse_test_data { @@ -248,6 +250,179 @@ static int gas_tests(void) } +static int sae_tests(void) +{ +#ifdef CONFIG_SAE + struct sae_data sae; + int ret = -1; + /* IEEE P802.11-REVmd/D2.1, Annex J.10 */ + const u8 addr1[ETH_ALEN] = { 0x82, 0x7b, 0x91, 0x9d, 0xd4, 0xb9 }; + const u8 addr2[ETH_ALEN] = { 0x1e, 0xec, 0x49, 0xea, 0x64, 0x88 }; + const char *pw = "mekmitasdigoat"; + const char *pwid = "psk4internet"; + const u8 local_rand[] = { + 0xa9, 0x06, 0xf6, 0x1e, 0x4d, 0x3a, 0x5d, 0x4e, + 0xb2, 0x96, 0x5f, 0xf3, 0x4c, 0xf9, 0x17, 0xdd, + 0x04, 0x44, 0x45, 0xc8, 0x78, 0xc1, 0x7c, 0xa5, + 0xd5, 0xb9, 0x37, 0x86, 0xda, 0x9f, 0x83, 0xcf + }; + const u8 local_mask[] = { + 0x42, 0x34, 0xb4, 0xfb, 0x17, 0xaa, 0x43, 0x5c, + 0x52, 0xfb, 0xfd, 0xeb, 0xe6, 0x40, 0x39, 0xb4, + 0x34, 0x78, 0x20, 0x0e, 0x54, 0xff, 0x7b, 0x6e, + 0x07, 0xb6, 0x9c, 0xad, 0x74, 0x15, 0x3c, 0x15 + }; + const u8 local_commit[] = { + 0x13, 0x00, 0xeb, 0x3b, 0xab, 0x19, 0x64, 0xe4, + 0xa0, 0xab, 0x05, 0x92, 0x5d, 0xdf, 0x33, 0x39, + 0x51, 0x91, 0x38, 0xbc, 0x65, 0xd6, 0xcd, 0xc0, + 0xf8, 0x13, 0xdd, 0x6f, 0xd4, 0x34, 0x4e, 0xb4, + 0xbf, 0xe4, 0x4b, 0x5c, 0x21, 0x59, 0x76, 0x58, + 0xf4, 0xe3, 0xed, 0xdf, 0xb4, 0xb9, 0x9f, 0x25, + 0xb4, 0xd6, 0x54, 0x0f, 0x32, 0xff, 0x1f, 0xd5, + 0xc5, 0x30, 0xc6, 0x0a, 0x79, 0x44, 0x48, 0x61, + 0x0b, 0xc6, 0xde, 0x3d, 0x92, 0xbd, 0xbb, 0xd4, + 0x7d, 0x93, 0x59, 0x80, 0xca, 0x6c, 0xf8, 0x98, + 0x8a, 0xb6, 0x63, 0x0b, 0xe6, 0x76, 0x4c, 0x88, + 0x5c, 0xeb, 0x97, 0x93, 0x97, 0x0f, 0x69, 0x52, + 0x17, 0xee, 0xff, 0x0d, 0x21, 0x70, 0x73, 0x6b, + 0x34, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74 + }; + const u8 peer_commit[] = { + 0x13, 0x00, 0x55, 0x64, 0xf0, 0x45, 0xb2, 0xea, + 0x1e, 0x56, 0x6c, 0xf1, 0xdd, 0x74, 0x1f, 0x70, + 0xd9, 0xbe, 0x35, 0xd2, 0xdf, 0x5b, 0x9a, 0x55, + 0x02, 0x94, 0x6e, 0xe0, 0x3c, 0xf8, 0xda, 0xe2, + 0x7e, 0x1e, 0x05, 0xb8, 0x43, 0x0e, 0xb7, 0xa9, + 0x9e, 0x24, 0x87, 0x7c, 0xe6, 0x9b, 0xaf, 0x3d, + 0xc5, 0x80, 0xe3, 0x09, 0x63, 0x3d, 0x6b, 0x38, + 0x5f, 0x83, 0xee, 0x1c, 0x3e, 0xc3, 0x59, 0x1f, + 0x1a, 0x53, 0x93, 0xc0, 0x6e, 0x80, 0x5d, 0xdc, + 0xeb, 0x2f, 0xde, 0x50, 0x93, 0x0d, 0xd7, 0xcf, + 0xeb, 0xb9, 0x87, 0xc6, 0xff, 0x96, 0x66, 0xaf, + 0x16, 0x4e, 0xb5, 0x18, 0x4d, 0x8e, 0x66, 0x62, + 0xed, 0x6a, 0xff, 0x0d, 0x21, 0x70, 0x73, 0x6b, + 0x34, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74 + }; + const u8 kck[] = { + 0x59, 0x9d, 0x6f, 0x1e, 0x27, 0x54, 0x8b, 0xe8, + 0x49, 0x9d, 0xce, 0xed, 0x2f, 0xec, 0xcf, 0x94, + 0x81, 0x8c, 0xe1, 0xc7, 0x9f, 0x1b, 0x4e, 0xb3, + 0xd6, 0xa5, 0x32, 0x28, 0xa0, 0x9b, 0xf3, 0xed + }; + const u8 pmk[] = { + 0x7a, 0xea, 0xd8, 0x6f, 0xba, 0x4c, 0x32, 0x21, + 0xfc, 0x43, 0x7f, 0x5f, 0x14, 0xd7, 0x0d, 0x85, + 0x4e, 0xa5, 0xd5, 0xaa, 0xc1, 0x69, 0x01, 0x16, + 0x79, 0x30, 0x81, 0xed, 0xa4, 0xd5, 0x57, 0xc5 + }; + const u8 pmkid[] = { + 0x40, 0xa0, 0x9b, 0x60, 0x17, 0xce, 0xbf, 0x00, + 0x72, 0x84, 0x3b, 0x53, 0x52, 0xaa, 0x2b, 0x4f + }; + const u8 local_confirm[] = { + 0x01, 0x00, 0x12, 0xd9, 0xd5, 0xc7, 0x8c, 0x50, + 0x05, 0x26, 0xd3, 0x6c, 0x41, 0xdb, 0xc5, 0x6a, + 0xed, 0xf2, 0x91, 0x4c, 0xed, 0xdd, 0xd7, 0xca, + 0xd4, 0xa5, 0x8c, 0x48, 0xf8, 0x3d, 0xbd, 0xe9, + 0xfc, 0x77 + }; + const u8 peer_confirm[] = { + 0x01, 0x00, 0x02, 0x87, 0x1c, 0xf9, 0x06, 0x89, + 0x8b, 0x80, 0x60, 0xec, 0x18, 0x41, 0x43, 0xbe, + 0x77, 0xb8, 0xc0, 0x8a, 0x80, 0x19, 0xb1, 0x3e, + 0xb6, 0xd0, 0xae, 0xf0, 0xd8, 0x38, 0x3d, 0xfa, + 0xc2, 0xfd + }; + struct wpabuf *buf = NULL; + struct crypto_bignum *mask = NULL; + + os_memset(&sae, 0, sizeof(sae)); + buf = wpabuf_alloc(1000); + if (!buf || + sae_set_group(&sae, 19) < 0 || + sae_prepare_commit(addr1, addr2, (const u8 *) pw, os_strlen(pw), + pwid, &sae) < 0) + goto fail; + + /* Override local values based on SAE test vector */ + crypto_bignum_deinit(sae.tmp->sae_rand, 1); + sae.tmp->sae_rand = crypto_bignum_init_set(local_rand, + sizeof(local_rand)); + mask = crypto_bignum_init_set(local_mask, sizeof(local_mask)); + if (!sae.tmp->sae_rand || !mask) + goto fail; + + if (crypto_bignum_add(sae.tmp->sae_rand, mask, + sae.tmp->own_commit_scalar) < 0 || + crypto_bignum_mod(sae.tmp->own_commit_scalar, sae.tmp->order, + sae.tmp->own_commit_scalar) < 0 || + crypto_ec_point_mul(sae.tmp->ec, sae.tmp->pwe_ecc, mask, + sae.tmp->own_commit_element_ecc) < 0 || + crypto_ec_point_invert(sae.tmp->ec, + sae.tmp->own_commit_element_ecc) < 0) + goto fail; + + /* Check that output matches the test vector */ + sae_write_commit(&sae, buf, NULL, pwid); + wpa_hexdump_buf(MSG_DEBUG, "SAE: Commit message", buf); + + if (wpabuf_len(buf) != sizeof(local_commit) || + os_memcmp(wpabuf_head(buf), local_commit, + sizeof(local_commit)) != 0) { + wpa_printf(MSG_ERROR, "SAE: Mismatch in local commit"); + goto fail; + } + + if (sae_parse_commit(&sae, peer_commit, sizeof(peer_commit), NULL, NULL, + NULL) != 0 || + sae_process_commit(&sae) < 0) + goto fail; + + if (os_memcmp(kck, sae.tmp->kck, SAE_KCK_LEN) != 0) { + wpa_printf(MSG_ERROR, "SAE: Mismatch in KCK"); + goto fail; + } + + if (os_memcmp(pmk, sae.pmk, SAE_PMK_LEN) != 0) { + wpa_printf(MSG_ERROR, "SAE: Mismatch in PMK"); + goto fail; + } + + if (os_memcmp(pmkid, sae.pmkid, SAE_PMKID_LEN) != 0) { + wpa_printf(MSG_ERROR, "SAE: Mismatch in PMKID"); + goto fail; + } + + buf->used = 0; + sae.send_confirm = 1; + sae_write_confirm(&sae, buf); + wpa_hexdump_buf(MSG_DEBUG, "SAE: Confirm message", buf); + + if (wpabuf_len(buf) != sizeof(local_confirm) || + os_memcmp(wpabuf_head(buf), local_confirm, + sizeof(local_confirm)) != 0) { + wpa_printf(MSG_ERROR, "SAE: Mismatch in local confirm"); + goto fail; + } + + if (sae_check_confirm(&sae, peer_confirm, sizeof(peer_confirm)) < 0) + goto fail; + + ret = 0; +fail: + sae_clear_data(&sae); + wpabuf_free(buf); + crypto_bignum_deinit(mask, 1); + return ret; +#else /* CONFIG_SAE */ + return 0; +#endif /* CONFIG_SAE */ +} + + int common_module_tests(void) { int ret = 0; @@ -256,6 +431,7 @@ int common_module_tests(void) if (ieee802_11_parse_tests() < 0 || gas_tests() < 0 || + sae_tests() < 0 || rsn_ie_parse_tests() < 0) ret = -1; diff --git a/src/common/defs.h b/src/common/defs.h index c968cd6cb82a5..4faf1c8601d04 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -59,6 +59,13 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean; #define WPA_KEY_MGMT_DPP BIT(23) #define WPA_KEY_MGMT_FT_IEEE8021X_SHA384 BIT(24) +#define WPA_KEY_MGMT_FT (WPA_KEY_MGMT_FT_PSK | \ + WPA_KEY_MGMT_FT_IEEE8021X | \ + WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | \ + WPA_KEY_MGMT_FT_SAE | \ + WPA_KEY_MGMT_FT_FILS_SHA256 | \ + WPA_KEY_MGMT_FT_FILS_SHA384) + static inline int wpa_key_mgmt_wpa_ieee8021x(int akm) { return !!(akm & (WPA_KEY_MGMT_IEEE8021X | @@ -86,12 +93,14 @@ static inline int wpa_key_mgmt_wpa_psk(int akm) static inline int wpa_key_mgmt_ft(int akm) { - return !!(akm & (WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_FT_IEEE8021X | - WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | - WPA_KEY_MGMT_FT_SAE | - WPA_KEY_MGMT_FT_FILS_SHA256 | - WPA_KEY_MGMT_FT_FILS_SHA384)); + return !!(akm & WPA_KEY_MGMT_FT); +} + +static inline int wpa_key_mgmt_only_ft(int akm) +{ + int ft = wpa_key_mgmt_ft(akm); + akm &= ~WPA_KEY_MGMT_FT; + return ft && !akm; } static inline int wpa_key_mgmt_ft_psk(int akm) @@ -399,4 +408,15 @@ enum eap_proxy_sim_state { #define OCE_STA_CFON BIT(1) #define OCE_AP BIT(2) +/* enum chan_width - Channel width definitions */ +enum chan_width { + CHAN_WIDTH_20_NOHT, + CHAN_WIDTH_20, + CHAN_WIDTH_40, + CHAN_WIDTH_80, + CHAN_WIDTH_80P80, + CHAN_WIDTH_160, + CHAN_WIDTH_UNKNOWN +}; + #endif /* DEFS_H */ diff --git a/src/common/dpp.c b/src/common/dpp.c index e715e0454d725..49de476973846 100644 --- a/src/common/dpp.c +++ b/src/common/dpp.c @@ -1,6 +1,7 @@ /* * DPP functionality shared between hostapd and wpa_supplicant * Copyright (c) 2017, Qualcomm Atheros, Inc. + * Copyright (c) 2018-2019, The Linux Foundation * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -18,6 +19,7 @@ #include "common/ieee802_11_common.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" +#include "common/gas.h" #include "crypto/crypto.h" #include "crypto/random.h" #include "crypto/aes.h" @@ -68,6 +70,11 @@ static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, #endif +struct dpp_global { + struct dl_list bootstrap; /* struct dpp_bootstrap_info */ + struct dl_list configurator; /* struct dpp_configurator */ +}; + static const struct dpp_curve_params dpp_curves[] = { /* The mandatory to support and the default NIST P-256 curve needs to * be the first entry on this list. */ @@ -813,7 +820,9 @@ static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info) const unsigned char *pk; int ppklen; X509_ALGOR *pa; -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20800000L) ASN1_OBJECT *pa_oid; #else const ASN1_OBJECT *pa_oid; @@ -1535,6 +1544,9 @@ static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth, 4 + sizeof(wrapped_data); if (neg_freq > 0) attr_len += 4 + 2; +#ifdef CONFIG_DPP2 + attr_len += 5; +#endif /* CONFIG_DPP2 */ #ifdef CONFIG_TESTING_OPTIONS if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) attr_len += 5; @@ -1577,6 +1589,13 @@ static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth, wpabuf_put_u8(msg, channel); } +#ifdef CONFIG_DPP2 + /* Protocol Version */ + wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, 2); +#endif /* CONFIG_DPP2 */ + #ifdef CONFIG_TESTING_OPTIONS if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) { wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); @@ -1703,6 +1722,9 @@ static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth, /* Build DPP Authentication Response frame attributes */ attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) + 4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data); +#ifdef CONFIG_DPP2 + attr_len += 5; +#endif /* CONFIG_DPP2 */ #ifdef CONFIG_TESTING_OPTIONS if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) attr_len += 5; @@ -1730,6 +1752,13 @@ static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth, wpabuf_put_buf(msg, pr); } +#ifdef CONFIG_DPP2 + /* Protocol Version */ + wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, 2); +#endif /* CONFIG_DPP2 */ + attr_end = wpabuf_put(msg, 0); #ifdef CONFIG_TESTING_OPTIONS @@ -2200,8 +2229,8 @@ fail: } -struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth, - const char *json) +static struct wpabuf * dpp_build_conf_req_attr(struct dpp_authentication *auth, + const char *json) { size_t nonce_len; size_t json_len, clear_len; @@ -2305,6 +2334,55 @@ fail: } +static void dpp_write_adv_proto(struct wpabuf *buf) +{ + /* Advertisement Protocol IE */ + wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(buf, 8); /* Length */ + wpabuf_put_u8(buf, 0x7f); + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 5); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, DPP_OUI_TYPE); + wpabuf_put_u8(buf, 0x01); +} + + +static void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query) +{ + /* GAS Query */ + wpabuf_put_le16(buf, wpabuf_len(query)); + wpabuf_put_buf(buf, query); +} + + +struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth, + const char *json) +{ + struct wpabuf *buf, *conf_req; + + conf_req = dpp_build_conf_req_attr(auth, json); + if (!conf_req) { + wpa_printf(MSG_DEBUG, + "DPP: No configuration request data available"); + return NULL; + } + + buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req)); + if (!buf) { + wpabuf_free(conf_req); + return NULL; + } + + dpp_write_adv_proto(buf); + dpp_write_gas_query(buf, conf_req); + wpabuf_free(conf_req); + wpa_hexdump_buf(MSG_MSGDUMP, "DPP: GAS Config Request", buf); + + return buf; +} + + static void dpp_auth_success(struct dpp_authentication *auth) { wpa_printf(MSG_DEBUG, @@ -2891,6 +2969,10 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual, u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len, i_bootstrap_len, channel_len; struct dpp_authentication *auth = NULL; +#ifdef CONFIG_DPP2 + const u8 *version; + u16 version_len; +#endif /* CONFIG_DPP2 */ #ifdef CONFIG_TESTING_OPTIONS if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) { @@ -2920,6 +3002,22 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual, auth->curve = own_bi->curve; auth->curr_freq = freq; + auth->peer_version = 1; /* default to the first version */ +#ifdef CONFIG_DPP2 + version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION, + &version_len); + if (version) { + if (version_len < 1 || version[0] == 0) { + dpp_auth_fail(auth, + "Invalid Protocol Version attribute"); + goto fail; + } + auth->peer_version = version[0]; + wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u", + auth->peer_version); + } +#endif /* CONFIG_DPP2 */ + channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL, &channel_len); if (channel) { @@ -3448,6 +3546,10 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr, wrapped2_len, r_auth_len; u8 r_auth2[DPP_MAX_HASH_LEN]; u8 role; +#ifdef CONFIG_DPP2 + const u8 *version; + u16 version_len; +#endif /* CONFIG_DPP2 */ #ifdef CONFIG_TESTING_OPTIONS if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) { @@ -3522,6 +3624,22 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr, return NULL; } + auth->peer_version = 1; /* default to the first version */ +#ifdef CONFIG_DPP2 + version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION, + &version_len); + if (version) { + if (version_len < 1 || version[0] == 0) { + dpp_auth_fail(auth, + "Invalid Protocol Version attribute"); + return NULL; + } + auth->peer_version = version[0]; + wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u", + auth->peer_version); + } +#endif /* CONFIG_DPP2 */ + status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS, &status_len); if (!status || status_len < 1) { @@ -3985,6 +4103,99 @@ fail: } +static int bin_str_eq(const char *val, size_t len, const char *cmp) +{ + return os_strlen(cmp) == len && os_memcmp(val, cmp, len) == 0; +} + + +struct dpp_configuration * dpp_configuration_alloc(const char *type) +{ + struct dpp_configuration *conf; + const char *end; + size_t len; + + conf = os_zalloc(sizeof(*conf)); + if (!conf) + goto fail; + + end = os_strchr(type, ' '); + if (end) + len = end - type; + else + len = os_strlen(type); + + if (bin_str_eq(type, len, "psk")) + conf->akm = DPP_AKM_PSK; + else if (bin_str_eq(type, len, "sae")) + conf->akm = DPP_AKM_SAE; + else if (bin_str_eq(type, len, "psk-sae") || + bin_str_eq(type, len, "psk+sae")) + conf->akm = DPP_AKM_PSK_SAE; + else if (bin_str_eq(type, len, "sae-dpp") || + bin_str_eq(type, len, "dpp+sae")) + conf->akm = DPP_AKM_SAE_DPP; + else if (bin_str_eq(type, len, "psk-sae-dpp") || + bin_str_eq(type, len, "dpp+psk+sae")) + conf->akm = DPP_AKM_PSK_SAE_DPP; + else if (bin_str_eq(type, len, "dpp")) + conf->akm = DPP_AKM_DPP; + else + goto fail; + + return conf; +fail: + dpp_configuration_free(conf); + return NULL; +} + + +int dpp_akm_psk(enum dpp_akm akm) +{ + return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE || + akm == DPP_AKM_PSK_SAE_DPP; +} + + +int dpp_akm_sae(enum dpp_akm akm) +{ + return akm == DPP_AKM_SAE || akm == DPP_AKM_PSK_SAE || + akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP; +} + + +int dpp_akm_legacy(enum dpp_akm akm) +{ + return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE || + akm == DPP_AKM_SAE; +} + + +int dpp_akm_dpp(enum dpp_akm akm) +{ + return akm == DPP_AKM_DPP || akm == DPP_AKM_SAE_DPP || + akm == DPP_AKM_PSK_SAE_DPP; +} + + +int dpp_akm_ver2(enum dpp_akm akm) +{ + return akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP; +} + + +int dpp_configuration_valid(const struct dpp_configuration *conf) +{ + if (conf->ssid_len == 0) + return 0; + if (dpp_akm_psk(conf->akm) && !conf->passphrase && !conf->psk_set) + return 0; + if (dpp_akm_sae(conf->akm) && !conf->passphrase) + return 0; + return 1; +} + + void dpp_configuration_free(struct dpp_configuration *conf) { if (!conf) @@ -3995,6 +4206,162 @@ void dpp_configuration_free(struct dpp_configuration *conf) } +static int dpp_configuration_parse(struct dpp_authentication *auth, + const char *cmd) +{ + const char *pos, *end; + struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL; + struct dpp_configuration *conf = NULL; + + pos = os_strstr(cmd, " conf=sta-"); + if (pos) { + conf_sta = dpp_configuration_alloc(pos + 10); + if (!conf_sta) + goto fail; + conf = conf_sta; + } + + pos = os_strstr(cmd, " conf=ap-"); + if (pos) { + conf_ap = dpp_configuration_alloc(pos + 9); + if (!conf_ap) + goto fail; + conf = conf_ap; + } + + if (!conf) + return 0; + + pos = os_strstr(cmd, " ssid="); + if (pos) { + pos += 6; + end = os_strchr(pos, ' '); + conf->ssid_len = end ? (size_t) (end - pos) : os_strlen(pos); + conf->ssid_len /= 2; + if (conf->ssid_len > sizeof(conf->ssid) || + hexstr2bin(pos, conf->ssid, conf->ssid_len) < 0) + goto fail; + } else { +#ifdef CONFIG_TESTING_OPTIONS + /* use a default SSID for legacy testing reasons */ + os_memcpy(conf->ssid, "test", 4); + conf->ssid_len = 4; +#else /* CONFIG_TESTING_OPTIONS */ + goto fail; +#endif /* CONFIG_TESTING_OPTIONS */ + } + + pos = os_strstr(cmd, " pass="); + if (pos) { + size_t pass_len; + + pos += 6; + end = os_strchr(pos, ' '); + pass_len = end ? (size_t) (end - pos) : os_strlen(pos); + pass_len /= 2; + if (pass_len > 63 || pass_len < 8) + goto fail; + conf->passphrase = os_zalloc(pass_len + 1); + if (!conf->passphrase || + hexstr2bin(pos, (u8 *) conf->passphrase, pass_len) < 0) + goto fail; + } + + pos = os_strstr(cmd, " psk="); + if (pos) { + pos += 5; + if (hexstr2bin(pos, conf->psk, PMK_LEN) < 0) + goto fail; + conf->psk_set = 1; + } + + pos = os_strstr(cmd, " group_id="); + if (pos) { + size_t group_id_len; + + pos += 10; + end = os_strchr(pos, ' '); + group_id_len = end ? (size_t) (end - pos) : os_strlen(pos); + conf->group_id = os_malloc(group_id_len + 1); + if (!conf->group_id) + goto fail; + os_memcpy(conf->group_id, pos, group_id_len); + conf->group_id[group_id_len] = '\0'; + } + + pos = os_strstr(cmd, " expiry="); + if (pos) { + long int val; + + pos += 8; + val = strtol(pos, NULL, 0); + if (val <= 0) + goto fail; + conf->netaccesskey_expiry = val; + } + + if (!dpp_configuration_valid(conf)) + goto fail; + + auth->conf_sta = conf_sta; + auth->conf_ap = conf_ap; + return 0; + +fail: + dpp_configuration_free(conf_sta); + dpp_configuration_free(conf_ap); + return -1; +} + + +static struct dpp_configurator * +dpp_configurator_get_id(struct dpp_global *dpp, unsigned int id) +{ + struct dpp_configurator *conf; + + if (!dpp) + return NULL; + + dl_list_for_each(conf, &dpp->configurator, + struct dpp_configurator, list) { + if (conf->id == id) + return conf; + } + return NULL; +} + + +int dpp_set_configurator(struct dpp_global *dpp, void *msg_ctx, + struct dpp_authentication *auth, + const char *cmd) +{ + const char *pos; + + if (!cmd) + return 0; + + wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd); + + pos = os_strstr(cmd, " configurator="); + if (pos) { + pos += 14; + auth->conf = dpp_configurator_get_id(dpp, atoi(pos)); + if (!auth->conf) { + wpa_printf(MSG_INFO, + "DPP: Could not find the specified configurator"); + return -1; + } + } + + if (dpp_configuration_parse(auth, cmd) < 0) { + wpa_msg(msg_ctx, MSG_INFO, + "DPP: Failed to set configurator parameters"); + return -1; + } + return 0; +} + + void dpp_auth_deinit(struct dpp_authentication *auth) { if (!auth) @@ -4094,6 +4461,31 @@ fail: } +static void dpp_build_legacy_cred_params(struct wpabuf *buf, + struct dpp_configuration *conf) +{ + if (conf->passphrase && os_strlen(conf->passphrase) < 64) { + char pass[63 * 6 + 1]; + + json_escape_string(pass, sizeof(pass), conf->passphrase, + os_strlen(conf->passphrase)); + wpabuf_put_str(buf, "\"pass\":\""); + wpabuf_put_str(buf, pass); + wpabuf_put_str(buf, "\""); + os_memset(pass, 0, sizeof(pass)); + } else if (conf->psk_set) { + char psk[2 * sizeof(conf->psk) + 1]; + + wpa_snprintf_hex(psk, sizeof(psk), + conf->psk, sizeof(conf->psk)); + wpabuf_put_str(buf, "\"psk_hex\":\""); + wpabuf_put_str(buf, psk); + wpabuf_put_str(buf, "\""); + os_memset(psk, 0, sizeof(psk)); + } +} + + static struct wpabuf * dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap, struct dpp_configuration *conf) @@ -4114,6 +4506,8 @@ dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap, const EVP_MD *sign_md; const BIGNUM *r, *s; size_t extra_len = 1000; + int incl_legacy; + enum dpp_akm akm; if (!auth->conf) { wpa_printf(MSG_INFO, @@ -4132,6 +4526,13 @@ dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap, goto fail; } + akm = conf->akm; + if (dpp_akm_ver2(akm) && auth->peer_version < 2) { + wpa_printf(MSG_DEBUG, + "DPP: Convert DPP+legacy credential to DPP-only for peer that does not support version 2"); + akm = DPP_AKM_DPP; + } + #ifdef CONFIG_TESTING_OPTIONS if (auth->groups_override) extra_len += os_strlen(auth->groups_override); @@ -4249,14 +4650,22 @@ skip_groups: if (!signed3) goto fail; + incl_legacy = dpp_akm_psk(akm) || dpp_akm_sae(akm); tailroom = 1000; tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid); tailroom += signed1_len + signed2_len + signed3_len; + if (incl_legacy) + tailroom += 1000; buf = dpp_build_conf_start(auth, conf, tailroom); if (!buf) goto fail; - wpabuf_put_str(buf, "\"cred\":{\"akm\":\"dpp\",\"signedConnector\":\""); + wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(akm)); + if (incl_legacy) { + dpp_build_legacy_cred_params(buf, conf); + wpabuf_put_str(buf, ","); + } + wpabuf_put_str(buf, "\"signedConnector\":\""); wpabuf_put_str(buf, signed1); wpabuf_put_u8(buf, '.'); wpabuf_put_str(buf, signed2); @@ -4302,28 +4711,7 @@ dpp_build_conf_obj_legacy(struct dpp_authentication *auth, int ap, return NULL; wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(conf->akm)); - if (conf->passphrase) { - char pass[63 * 6 + 1]; - - if (os_strlen(conf->passphrase) > 63) { - wpabuf_free(buf); - return NULL; - } - - json_escape_string(pass, sizeof(pass), conf->passphrase, - os_strlen(conf->passphrase)); - wpabuf_put_str(buf, "\"pass\":\""); - wpabuf_put_str(buf, pass); - wpabuf_put_str(buf, "\""); - } else { - char psk[2 * sizeof(conf->psk) + 1]; - - wpa_snprintf_hex(psk, sizeof(psk), - conf->psk, sizeof(conf->psk)); - wpabuf_put_str(buf, "\"psk_hex\":\""); - wpabuf_put_str(buf, psk); - wpabuf_put_str(buf, "\""); - } + dpp_build_legacy_cred_params(buf, conf); wpabuf_put_str(buf, "}}"); wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)", @@ -4354,7 +4742,7 @@ dpp_build_conf_obj(struct dpp_authentication *auth, int ap) return NULL; } - if (conf->akm == DPP_AKM_DPP) + if (dpp_akm_dpp(conf->akm)) return dpp_build_conf_obj_dpp(auth, ap, conf); return dpp_build_conf_obj_legacy(auth, ap, conf); } @@ -4378,6 +4766,7 @@ dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce, wpabuf_head(conf), wpabuf_len(conf)); } status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE; + auth->conf_resp_status = status; /* { E-nonce, configurationObject}ke */ clear_len = 4 + e_nonce_len; @@ -4550,6 +4939,7 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, goto fail; } wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); + os_memcpy(auth->e_nonce, e_nonce, e_nonce_len); config_attr = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_CONFIG_ATTR_OBJ, @@ -4714,7 +5104,7 @@ static int dpp_parse_cred_legacy(struct dpp_authentication *auth, os_strlcpy(auth->passphrase, pass->string, sizeof(auth->passphrase)); } else if (psk_hex && psk_hex->type == JSON_STRING) { - if (auth->akm == DPP_AKM_SAE) { + if (dpp_akm_sae(auth->akm) && !dpp_akm_psk(auth->akm)) { wpa_printf(MSG_DEBUG, "DPP: Unexpected psk_hex with akm=sae"); return -1; @@ -4732,8 +5122,7 @@ static int dpp_parse_cred_legacy(struct dpp_authentication *auth, return -1; } - if ((auth->akm == DPP_AKM_SAE || auth->akm == DPP_AKM_PSK_SAE) && - !auth->passphrase[0]) { + if (dpp_akm_sae(auth->akm) && !auth->passphrase[0]) { wpa_printf(MSG_DEBUG, "DPP: No pass for sae found"); return -1; } @@ -5246,6 +5635,13 @@ static int dpp_parse_cred_dpp(struct dpp_authentication *auth, os_memset(&info, 0, sizeof(info)); + if (dpp_akm_psk(auth->akm) || dpp_akm_sae(auth->akm)) { + wpa_printf(MSG_DEBUG, + "DPP: Legacy credential included in Connector credential"); + if (dpp_parse_cred_legacy(auth, cred) < 0) + return -1; + } + wpa_printf(MSG_DEBUG, "DPP: Connector credential"); csign = json_get_member(cred, "csign"); @@ -5311,6 +5707,10 @@ const char * dpp_akm_str(enum dpp_akm akm) return "sae"; case DPP_AKM_PSK_SAE: return "psk+sae"; + case DPP_AKM_SAE_DPP: + return "dpp+sae"; + case DPP_AKM_PSK_SAE_DPP: + return "dpp+psk+sae"; default: return "??"; } @@ -5327,6 +5727,10 @@ static enum dpp_akm dpp_akm_from_str(const char *akm) return DPP_AKM_PSK_SAE; if (os_strcmp(akm, "dpp") == 0) return DPP_AKM_DPP; + if (os_strcmp(akm, "dpp+sae") == 0) + return DPP_AKM_SAE_DPP; + if (os_strcmp(akm, "dpp+psk+sae") == 0) + return DPP_AKM_PSK_SAE_DPP; return DPP_AKM_UNKNOWN; } @@ -5390,11 +5794,10 @@ static int dpp_parse_conf_obj(struct dpp_authentication *auth, } auth->akm = dpp_akm_from_str(token->string); - if (auth->akm == DPP_AKM_PSK || auth->akm == DPP_AKM_SAE || - auth->akm == DPP_AKM_PSK_SAE) { + if (dpp_akm_legacy(auth->akm)) { if (dpp_parse_cred_legacy(auth, cred) < 0) goto fail; - } else if (auth->akm == DPP_AKM_DPP) { + } else if (dpp_akm_dpp(auth->akm)) { if (dpp_parse_cred_dpp(auth, cred) < 0) goto fail; } else { @@ -5423,6 +5826,8 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth, size_t unwrapped_len = 0; int ret = -1; + auth->conf_resp_status = 255; + if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) { dpp_auth_fail(auth, "Invalid attribute in config response"); return -1; @@ -5483,6 +5888,7 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth, "Missing or invalid required DPP Status attribute"); goto fail; } + auth->conf_resp_status = status[0]; wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); if (status[0] != DPP_STATUS_OK) { dpp_auth_fail(auth, "Configurator rejected configuration"); @@ -5509,6 +5915,146 @@ fail: } +#ifdef CONFIG_DPP2 +enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth, + const u8 *hdr, + const u8 *attr_start, size_t attr_len) +{ + const u8 *wrapped_data, *status, *e_nonce; + u16 wrapped_data_len, status_len, e_nonce_len; + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + enum dpp_status_error ret = 256; + + wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + dpp_auth_fail(auth, + "Missing or invalid required Wrapped Data attribute"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", + wrapped_data, wrapped_data_len); + + attr_len = wrapped_data - 4 - attr_start; + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + addr[1] = attr_start; + len[1] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + e_nonce = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_ENROLLEE_NONCE, + &e_nonce_len); + if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, + "Missing or invalid Enrollee Nonce attribute"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); + if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) { + dpp_auth_fail(auth, "Enrollee Nonce mismatch"); + wpa_hexdump(MSG_DEBUG, "DPP: Expected Enrollee Nonce", + auth->e_nonce, e_nonce_len); + goto fail; + } + + status = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_STATUS, + &status_len); + if (!status || status_len < 1) { + dpp_auth_fail(auth, + "Missing or invalid required DPP Status attribute"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); + ret = status[0]; + +fail: + bin_clear_free(unwrapped, unwrapped_len); + return ret; +} +#endif /* CONFIG_DPP2 */ + + +struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth, + enum dpp_status_error status) +{ + struct wpabuf *msg, *clear; + size_t nonce_len, clear_len, attr_len; + const u8 *addr[2]; + size_t len[2]; + u8 *wrapped; + + nonce_len = auth->curve->nonce_len; + clear_len = 5 + 4 + nonce_len; + attr_len = 4 + clear_len + AES_BLOCK_SIZE; + clear = wpabuf_alloc(clear_len); + msg = dpp_alloc_msg(DPP_PA_CONFIGURATION_RESULT, attr_len); + if (!clear || !msg) + return NULL; + + /* DPP Status */ + dpp_build_attr_status(clear, status); + + /* E-nonce */ + wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); + wpabuf_put_le16(clear, nonce_len); + wpabuf_put_data(clear, auth->e_nonce, nonce_len); + + /* OUI, OUI type, Crypto Suite, DPP frame type */ + addr[0] = wpabuf_head_u8(msg) + 2; + len[0] = 3 + 1 + 1 + 1; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + + /* Attributes before Wrapped Data (none) */ + addr[1] = wpabuf_put(msg, 0); + len[1] = 0; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + /* Wrapped Data */ + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + + wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); + if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, + wpabuf_head(clear), wpabuf_len(clear), + 2, addr, len, wrapped) < 0) + goto fail; + + wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Result attributes", msg); + wpabuf_free(clear); + return msg; +fail: + wpabuf_free(clear); + wpabuf_free(msg); + return NULL; +} + + void dpp_configurator_free(struct dpp_configurator *conf) { if (!conf) @@ -7689,3 +8235,487 @@ fail: goto out; } #endif /* CONFIG_TESTING_OPTIONS */ + + +#ifdef CONFIG_DPP2 + +struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key, + size_t net_access_key_len) +{ + struct wpabuf *pub = NULL; + EVP_PKEY *own_key; + struct dpp_pfs *pfs; + + pfs = os_zalloc(sizeof(*pfs)); + if (!pfs) + return NULL; + + own_key = dpp_set_keypair(&pfs->curve, net_access_key, + net_access_key_len); + if (!own_key) { + wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey"); + goto fail; + } + EVP_PKEY_free(own_key); + + pfs->ecdh = crypto_ecdh_init(pfs->curve->ike_group); + if (!pfs->ecdh) + goto fail; + + pub = crypto_ecdh_get_pubkey(pfs->ecdh, 0); + pub = wpabuf_zeropad(pub, pfs->curve->prime_len); + if (!pub) + goto fail; + + pfs->ie = wpabuf_alloc(5 + wpabuf_len(pub)); + if (!pfs->ie) + goto fail; + wpabuf_put_u8(pfs->ie, WLAN_EID_EXTENSION); + wpabuf_put_u8(pfs->ie, 1 + 2 + wpabuf_len(pub)); + wpabuf_put_u8(pfs->ie, WLAN_EID_EXT_OWE_DH_PARAM); + wpabuf_put_le16(pfs->ie, pfs->curve->ike_group); + wpabuf_put_buf(pfs->ie, pub); + wpabuf_free(pub); + wpa_hexdump_buf(MSG_DEBUG, "DPP: Diffie-Hellman Parameter element", + pfs->ie); + + return pfs; +fail: + wpabuf_free(pub); + dpp_pfs_free(pfs); + return NULL; +} + + +int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len) +{ + if (peer_ie_len < 2) + return -1; + if (WPA_GET_LE16(peer_ie) != pfs->curve->ike_group) { + wpa_printf(MSG_DEBUG, "DPP: Peer used different group for PFS"); + return -1; + } + + pfs->secret = crypto_ecdh_set_peerkey(pfs->ecdh, 0, peer_ie + 2, + peer_ie_len - 2); + pfs->secret = wpabuf_zeropad(pfs->secret, pfs->curve->prime_len); + if (!pfs->secret) { + wpa_printf(MSG_DEBUG, "DPP: Invalid peer DH public key"); + return -1; + } + wpa_hexdump_buf_key(MSG_DEBUG, "DPP: DH shared secret", pfs->secret); + return 0; +} + + +void dpp_pfs_free(struct dpp_pfs *pfs) +{ + if (!pfs) + return; + crypto_ecdh_deinit(pfs->ecdh); + wpabuf_free(pfs->ie); + wpabuf_clear_free(pfs->secret); + os_free(pfs); +} + +#endif /* CONFIG_DPP2 */ + + +static unsigned int dpp_next_id(struct dpp_global *dpp) +{ + struct dpp_bootstrap_info *bi; + unsigned int max_id = 0; + + dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { + if (bi->id > max_id) + max_id = bi->id; + } + return max_id + 1; +} + + +static int dpp_bootstrap_del(struct dpp_global *dpp, unsigned int id) +{ + struct dpp_bootstrap_info *bi, *tmp; + int found = 0; + + if (!dpp) + return -1; + + dl_list_for_each_safe(bi, tmp, &dpp->bootstrap, + struct dpp_bootstrap_info, list) { + if (id && bi->id != id) + continue; + found = 1; + dl_list_del(&bi->list); + dpp_bootstrap_info_free(bi); + } + + if (id == 0) + return 0; /* flush succeeds regardless of entries found */ + return found ? 0 : -1; +} + + +struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp, + const char *uri) +{ + struct dpp_bootstrap_info *bi; + + if (!dpp) + return NULL; + + bi = dpp_parse_qr_code(uri); + if (!bi) + return NULL; + + bi->id = dpp_next_id(dpp); + dl_list_add(&dpp->bootstrap, &bi->list); + return bi; +} + + +int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd) +{ + char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL; + char *key = NULL; + u8 *privkey = NULL; + size_t privkey_len = 0; + size_t len; + int ret = -1; + struct dpp_bootstrap_info *bi; + + if (!dpp) + return -1; + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + goto fail; + + if (os_strstr(cmd, "type=qrcode")) + bi->type = DPP_BOOTSTRAP_QR_CODE; + else if (os_strstr(cmd, "type=pkex")) + bi->type = DPP_BOOTSTRAP_PKEX; + else + goto fail; + + chan = get_param(cmd, " chan="); + mac = get_param(cmd, " mac="); + info = get_param(cmd, " info="); + curve = get_param(cmd, " curve="); + key = get_param(cmd, " key="); + + if (key) { + privkey_len = os_strlen(key) / 2; + privkey = os_malloc(privkey_len); + if (!privkey || + hexstr2bin(key, privkey, privkey_len) < 0) + goto fail; + } + + pk = dpp_keygen(bi, curve, privkey, privkey_len); + if (!pk) + goto fail; + + len = 4; /* "DPP:" */ + if (chan) { + if (dpp_parse_uri_chan_list(bi, chan) < 0) + goto fail; + len += 3 + os_strlen(chan); /* C:...; */ + } + if (mac) { + if (dpp_parse_uri_mac(bi, mac) < 0) + goto fail; + len += 3 + os_strlen(mac); /* M:...; */ + } + if (info) { + if (dpp_parse_uri_info(bi, info) < 0) + goto fail; + len += 3 + os_strlen(info); /* I:...; */ + } + len += 4 + os_strlen(pk); + bi->uri = os_malloc(len + 1); + if (!bi->uri) + goto fail; + os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;", + chan ? "C:" : "", chan ? chan : "", chan ? ";" : "", + mac ? "M:" : "", mac ? mac : "", mac ? ";" : "", + info ? "I:" : "", info ? info : "", info ? ";" : "", + pk); + bi->id = dpp_next_id(dpp); + dl_list_add(&dpp->bootstrap, &bi->list); + ret = bi->id; + bi = NULL; +fail: + os_free(curve); + os_free(pk); + os_free(chan); + os_free(mac); + os_free(info); + str_clear_free(key); + bin_clear_free(privkey, privkey_len); + dpp_bootstrap_info_free(bi); + return ret; +} + + +struct dpp_bootstrap_info * +dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id) +{ + struct dpp_bootstrap_info *bi; + + if (!dpp) + return NULL; + + dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { + if (bi->id == id) + return bi; + } + return NULL; +} + + +int dpp_bootstrap_remove(struct dpp_global *dpp, const char *id) +{ + unsigned int id_val; + + if (os_strcmp(id, "*") == 0) { + id_val = 0; + } else { + id_val = atoi(id); + if (id_val == 0) + return -1; + } + + return dpp_bootstrap_del(dpp, id_val); +} + + +struct dpp_bootstrap_info * +dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer, + unsigned int freq) +{ + struct dpp_bootstrap_info *bi; + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + return NULL; + bi->id = dpp_next_id(dpp); + bi->type = DPP_BOOTSTRAP_PKEX; + os_memcpy(bi->mac_addr, peer, ETH_ALEN); + bi->num_freq = 1; + bi->freq[0] = freq; + bi->curve = pkex->own_bi->curve; + bi->pubkey = pkex->peer_bootstrap_key; + pkex->peer_bootstrap_key = NULL; + if (dpp_bootstrap_key_hash(bi) < 0) { + dpp_bootstrap_info_free(bi); + return NULL; + } + dpp_pkex_free(pkex); + dl_list_add(&dpp->bootstrap, &bi->list); + return bi; +} + + +const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id) +{ + struct dpp_bootstrap_info *bi; + + bi = dpp_bootstrap_get_id(dpp, id); + if (!bi) + return NULL; + return bi->uri; +} + + +int dpp_bootstrap_info(struct dpp_global *dpp, int id, + char *reply, int reply_size) +{ + struct dpp_bootstrap_info *bi; + + bi = dpp_bootstrap_get_id(dpp, id); + if (!bi) + return -1; + return os_snprintf(reply, reply_size, "type=%s\n" + "mac_addr=" MACSTR "\n" + "info=%s\n" + "num_freq=%u\n" + "curve=%s\n", + dpp_bootstrap_type_txt(bi->type), + MAC2STR(bi->mac_addr), + bi->info ? bi->info : "", + bi->num_freq, + bi->curve->name); +} + + +void dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap, + const u8 *r_bootstrap, + struct dpp_bootstrap_info **own_bi, + struct dpp_bootstrap_info **peer_bi) +{ + struct dpp_bootstrap_info *bi; + + *own_bi = NULL; + *peer_bi = NULL; + if (!dpp) + return; + + dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { + if (!*own_bi && bi->own && + os_memcmp(bi->pubkey_hash, r_bootstrap, + SHA256_MAC_LEN) == 0) { + wpa_printf(MSG_DEBUG, + "DPP: Found matching own bootstrapping information"); + *own_bi = bi; + } + + if (!*peer_bi && !bi->own && + os_memcmp(bi->pubkey_hash, i_bootstrap, + SHA256_MAC_LEN) == 0) { + wpa_printf(MSG_DEBUG, + "DPP: Found matching peer bootstrapping information"); + *peer_bi = bi; + } + + if (*own_bi && *peer_bi) + break; + } + +} + + +static unsigned int dpp_next_configurator_id(struct dpp_global *dpp) +{ + struct dpp_configurator *conf; + unsigned int max_id = 0; + + dl_list_for_each(conf, &dpp->configurator, struct dpp_configurator, + list) { + if (conf->id > max_id) + max_id = conf->id; + } + return max_id + 1; +} + + +int dpp_configurator_add(struct dpp_global *dpp, const char *cmd) +{ + char *curve = NULL; + char *key = NULL; + u8 *privkey = NULL; + size_t privkey_len = 0; + int ret = -1; + struct dpp_configurator *conf = NULL; + + curve = get_param(cmd, " curve="); + key = get_param(cmd, " key="); + + if (key) { + privkey_len = os_strlen(key) / 2; + privkey = os_malloc(privkey_len); + if (!privkey || + hexstr2bin(key, privkey, privkey_len) < 0) + goto fail; + } + + conf = dpp_keygen_configurator(curve, privkey, privkey_len); + if (!conf) + goto fail; + + conf->id = dpp_next_configurator_id(dpp); + dl_list_add(&dpp->configurator, &conf->list); + ret = conf->id; + conf = NULL; +fail: + os_free(curve); + str_clear_free(key); + bin_clear_free(privkey, privkey_len); + dpp_configurator_free(conf); + return ret; +} + + +static int dpp_configurator_del(struct dpp_global *dpp, unsigned int id) +{ + struct dpp_configurator *conf, *tmp; + int found = 0; + + if (!dpp) + return -1; + + dl_list_for_each_safe(conf, tmp, &dpp->configurator, + struct dpp_configurator, list) { + if (id && conf->id != id) + continue; + found = 1; + dl_list_del(&conf->list); + dpp_configurator_free(conf); + } + + if (id == 0) + return 0; /* flush succeeds regardless of entries found */ + return found ? 0 : -1; +} + + +int dpp_configurator_remove(struct dpp_global *dpp, const char *id) +{ + unsigned int id_val; + + if (os_strcmp(id, "*") == 0) { + id_val = 0; + } else { + id_val = atoi(id); + if (id_val == 0) + return -1; + } + + return dpp_configurator_del(dpp, id_val); +} + + +int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id, + char *buf, size_t buflen) +{ + struct dpp_configurator *conf; + + conf = dpp_configurator_get_id(dpp, id); + if (!conf) + return -1; + + return dpp_configurator_get_key(conf, buf, buflen); +} + + +struct dpp_global * dpp_global_init(void) +{ + struct dpp_global *dpp; + + dpp = os_zalloc(sizeof(*dpp)); + if (!dpp) + return NULL; + + dl_list_init(&dpp->bootstrap); + dl_list_init(&dpp->configurator); + + return dpp; +} + + +void dpp_global_clear(struct dpp_global *dpp) +{ + if (!dpp) + return; + + dpp_bootstrap_del(dpp, 0); + dpp_configurator_del(dpp, 0); +} + + +void dpp_global_deinit(struct dpp_global *dpp) +{ + dpp_global_clear(dpp); + os_free(dpp); +} diff --git a/src/common/dpp.h b/src/common/dpp.h index 25759088a57ef..5a6d8cc79c2cf 100644 --- a/src/common/dpp.h +++ b/src/common/dpp.h @@ -1,6 +1,7 @@ /* * DPP functionality shared between hostapd and wpa_supplicant * Copyright (c) 2017, Qualcomm Atheros, Inc. + * Copyright (c) 2018-2019, The Linux Foundation * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,12 +10,16 @@ #ifndef DPP_H #define DPP_H +#ifdef CONFIG_DPP #include <openssl/x509.h> #include "utils/list.h" #include "common/wpa_common.h" #include "crypto/sha256.h" +struct crypto_ecdh; +struct dpp_global; + #define DPP_HDR_LEN (4 + 2) /* OUI, OUI Type, Crypto Suite, DPP frame type */ enum dpp_public_action_frame_type { @@ -27,6 +32,7 @@ enum dpp_public_action_frame_type { DPP_PA_PKEX_EXCHANGE_RESP = 8, DPP_PA_PKEX_COMMIT_REVEAL_REQ = 9, DPP_PA_PKEX_COMMIT_REVEAL_RESP = 10, + DPP_PA_CONFIGURATION_RESULT = 11, }; enum dpp_attribute_id { @@ -54,6 +60,8 @@ enum dpp_attribute_id { DPP_ATTR_TRANSACTION_ID = 0x1016, DPP_ATTR_BOOTSTRAP_INFO = 0x1017, DPP_ATTR_CHANNEL = 0x1018, + DPP_ATTR_PROTOCOL_VERSION = 0x1019, + DPP_ATTR_ENVELOPED_DATA = 0x101A, }; enum dpp_status_error { @@ -66,6 +74,7 @@ enum dpp_status_error { DPP_STATUS_RESPONSE_PENDING = 6, DPP_STATUS_INVALID_CONNECTOR = 7, DPP_STATUS_NO_MATCH = 8, + DPP_STATUS_CONFIG_REJECTED = 9, }; #define DPP_CAPAB_ENROLLEE BIT(0) @@ -141,7 +150,9 @@ enum dpp_akm { DPP_AKM_DPP, DPP_AKM_PSK, DPP_AKM_SAE, - DPP_AKM_PSK_SAE + DPP_AKM_PSK_SAE, + DPP_AKM_SAE_DPP, + DPP_AKM_PSK_SAE_DPP, }; struct dpp_configuration { @@ -158,10 +169,12 @@ struct dpp_configuration { /* For legacy configuration */ char *passphrase; u8 psk[32]; + int psk_set; }; struct dpp_authentication { void *msg_ctx; + u8 peer_version; const struct dpp_curve_params *curve; struct dpp_bootstrap_info *peer_bi; struct dpp_bootstrap_info *own_bi; @@ -169,6 +182,7 @@ struct dpp_authentication { u8 waiting_pubkey_hash[SHA256_MAC_LEN]; int response_pending; enum dpp_status_error auth_resp_status; + enum dpp_status_error conf_resp_status; u8 peer_mac_addr[ETH_ALEN]; u8 i_nonce[DPP_MAX_NONCE_LEN]; u8 r_nonce[DPP_MAX_NONCE_LEN]; @@ -204,6 +218,8 @@ struct dpp_authentication { u8 allowed_roles; int configurator; int remove_on_tx_status; + int connect_on_tx_status; + int waiting_conf_result; int auth_success; struct wpabuf *conf_req; const struct wpabuf *conf_resp; /* owned by GAS server */ @@ -336,6 +352,7 @@ enum dpp_test_behavior { DPP_TEST_STOP_AT_AUTH_RESP = 88, DPP_TEST_STOP_AT_AUTH_CONF = 89, DPP_TEST_STOP_AT_CONF_REQ = 90, + DPP_TEST_REJECT_CONFIG = 91, }; extern enum dpp_test_behavior dpp_test; @@ -382,13 +399,28 @@ int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr, const u8 *attr_start, size_t attr_len); int dpp_notify_new_qr_code(struct dpp_authentication *auth, struct dpp_bootstrap_info *peer_bi); +struct dpp_configuration * dpp_configuration_alloc(const char *type); +int dpp_akm_psk(enum dpp_akm akm); +int dpp_akm_sae(enum dpp_akm akm); +int dpp_akm_legacy(enum dpp_akm akm); +int dpp_akm_dpp(enum dpp_akm akm); +int dpp_akm_ver2(enum dpp_akm akm); +int dpp_configuration_valid(const struct dpp_configuration *conf); void dpp_configuration_free(struct dpp_configuration *conf); +int dpp_set_configurator(struct dpp_global *dpp, void *msg_ctx, + struct dpp_authentication *auth, + const char *cmd); void dpp_auth_deinit(struct dpp_authentication *auth); struct wpabuf * dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, size_t attr_len); int dpp_conf_resp_rx(struct dpp_authentication *auth, const struct wpabuf *resp); +enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth, + const u8 *hdr, + const u8 *attr_start, size_t attr_len); +struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth, + enum dpp_status_error status); struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type, size_t len); const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len); @@ -432,4 +464,42 @@ void dpp_pkex_free(struct dpp_pkex *pkex); char * dpp_corrupt_connector_signature(const char *connector); + +struct dpp_pfs { + struct crypto_ecdh *ecdh; + const struct dpp_curve_params *curve; + struct wpabuf *ie; + struct wpabuf *secret; +}; + +struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key, + size_t net_access_key_len); +int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len); +void dpp_pfs_free(struct dpp_pfs *pfs); + +struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp, + const char *uri); +int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd); +struct dpp_bootstrap_info * +dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id); +int dpp_bootstrap_remove(struct dpp_global *dpp, const char *id); +struct dpp_bootstrap_info * +dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer, + unsigned int freq); +const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id); +int dpp_bootstrap_info(struct dpp_global *dpp, int id, + char *reply, int reply_size); +void dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap, + const u8 *r_bootstrap, + struct dpp_bootstrap_info **own_bi, + struct dpp_bootstrap_info **peer_bi); +int dpp_configurator_add(struct dpp_global *dpp, const char *cmd); +int dpp_configurator_remove(struct dpp_global *dpp, const char *id); +int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id, + char *buf, size_t buflen); +struct dpp_global * dpp_global_init(void); +void dpp_global_clear(struct dpp_global *dpp); +void dpp_global_deinit(struct dpp_global *dpp); + +#endif /* CONFIG_DPP */ #endif /* DPP_H */ diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c index db40379271850..49ed80657521a 100644 --- a/src/common/hw_features_common.c +++ b/src/common/hw_features_common.c @@ -87,13 +87,29 @@ int hw_get_chan(struct hostapd_hw_modes *mode, int freq) int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan, int sec_chan) { - int ok, j, first; + int ok, first; int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140, 149, 157, 165, 184, 192 }; size_t k; + struct hostapd_channel_data *p_chan, *s_chan; + const int ht40_plus = pri_chan < sec_chan; - if (pri_chan == sec_chan || !sec_chan) - return 1; /* HT40 not used */ + p_chan = hw_get_channel_chan(mode, pri_chan, NULL); + if (!p_chan) + return 0; + + if (pri_chan == sec_chan || !sec_chan) { + if (chan_pri_allowed(p_chan)) + return 1; /* HT40 not used */ + + wpa_printf(MSG_ERROR, "Channel %d is not allowed as primary", + pri_chan); + return 0; + } + + s_chan = hw_get_channel_chan(mode, sec_chan, NULL); + if (!s_chan) + return 0; wpa_printf(MSG_DEBUG, "HT40: control channel: %d secondary channel: %d", @@ -101,16 +117,9 @@ int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan, /* Verify that HT40 secondary channel is an allowed 20 MHz * channel */ - ok = 0; - for (j = 0; j < mode->num_channels; j++) { - struct hostapd_channel_data *chan = &mode->channels[j]; - if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && - chan->chan == sec_chan) { - ok = 1; - break; - } - } - if (!ok) { + if ((s_chan->flag & HOSTAPD_CHAN_DISABLED) || + (ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) || + (!ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M))) { wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed", sec_chan); return 0; @@ -553,3 +562,59 @@ int ieee80211ac_cap_check(u32 hw, u32 conf) } #endif /* CONFIG_IEEE80211AC */ + + +u32 num_chan_to_bw(int num_chans) +{ + switch (num_chans) { + case 2: + case 4: + case 8: + return num_chans * 20; + default: + return 20; + } +} + + +/* check if BW is applicable for channel */ +int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw, + int ht40_plus, int pri) +{ + u32 bw_mask; + + switch (bw) { + case 20: + bw_mask = HOSTAPD_CHAN_WIDTH_20; + break; + case 40: + /* HT 40 MHz support declared only for primary channel, + * just skip 40 MHz secondary checking */ + if (pri && ht40_plus) + bw_mask = HOSTAPD_CHAN_WIDTH_40P; + else if (pri && !ht40_plus) + bw_mask = HOSTAPD_CHAN_WIDTH_40M; + else + bw_mask = 0; + break; + case 80: + bw_mask = HOSTAPD_CHAN_WIDTH_80; + break; + case 160: + bw_mask = HOSTAPD_CHAN_WIDTH_160; + break; + default: + bw_mask = 0; + break; + } + + return (chan->allowed_bw & bw_mask) == bw_mask; +} + + +/* check if channel is allowed to be used as primary */ +int chan_pri_allowed(const struct hostapd_channel_data *chan) +{ + return !(chan->flag & HOSTAPD_CHAN_DISABLED) && + (chan->allowed_bw & HOSTAPD_CHAN_WIDTH_20); +} diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h index 9cddbd50e56a9..eb1f1c57f10f5 100644 --- a/src/common/hw_features_common.h +++ b/src/common/hw_features_common.h @@ -39,4 +39,9 @@ void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps, int disabled); int ieee80211ac_cap_check(u32 hw, u32 conf); +u32 num_chan_to_bw(int num_chans); +int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw, + int ht40_plus, int pri); +int chan_pri_allowed(const struct hostapd_channel_data *chan); + #endif /* HW_FEATURES_COMMON_H */ diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index e1ef27795b99a..e42a327449eb8 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-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -126,6 +126,10 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->roaming_cons_sel = pos; elems->roaming_cons_sel_len = elen; break; + case MULTI_AP_OUI_TYPE: + elems->multi_ap = pos; + elems->multi_ap_len = elen; + break; default: wpa_printf(MSG_MSGDUMP, "Unknown WFA " "information element ignored " @@ -266,6 +270,14 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, elems->password_id = pos; elems->password_id_len = elen; break; + case WLAN_EID_EXT_HE_CAPABILITIES: + elems->he_capabilities = pos; + elems->he_capabilities_len = elen; + break; + case WLAN_EID_EXT_OCV_OCI: + elems->oci = pos; + elems->oci_len = elen; + break; default: if (show_errors) { wpa_printf(MSG_MSGDUMP, @@ -291,29 +303,17 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, struct ieee802_11_elems *elems, int show_errors) { - size_t left = len; - const u8 *pos = start; + const struct element *elem; int unknown = 0; os_memset(elems, 0, sizeof(*elems)); - while (left >= 2) { - u8 id, elen; + if (!start) + return ParseOK; - id = *pos++; - elen = *pos++; - left -= 2; - - if (elen > left) { - if (show_errors) { - wpa_printf(MSG_DEBUG, "IEEE 802.11 element " - "parse failed (id=%d elen=%d " - "left=%lu)", - id, elen, (unsigned long) left); - wpa_hexdump(MSG_MSGDUMP, "IEs", start, len); - } - return ParseFailed; - } + for_each_element(elem, start, len) { + u8 id = elem->id, elen = elem->datalen; + const u8 *pos = elem->data; switch (id) { case WLAN_EID_SSID: @@ -461,8 +461,7 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->mic = pos; elems->mic_len = elen; /* after mic everything is encrypted, so stop. */ - left = elen; - break; + goto done; case WLAN_EID_MULTI_BAND: if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) { wpa_printf(MSG_MSGDUMP, @@ -521,35 +520,33 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, id, elen); break; } - - left -= elen; - pos += elen; } - if (left) + if (!for_each_element_completed(elem, start, len)) { + if (show_errors) { + wpa_printf(MSG_DEBUG, + "IEEE 802.11 element parse failed @%d", + (int) (start + len - (const u8 *) elem)); + wpa_hexdump(MSG_MSGDUMP, "IEs", start, len); + } return ParseFailed; + } +done: return unknown ? ParseUnknown : ParseOK; } int ieee802_11_ie_count(const u8 *ies, size_t ies_len) { + const struct element *elem; int count = 0; - const u8 *pos, *end; if (ies == NULL) return 0; - pos = ies; - end = ies + ies_len; - - while (end - pos >= 2) { - if (2 + pos[1] > end - pos) - break; + for_each_element(elem, ies, ies_len) count++; - pos += 2 + pos[1]; - } return count; } @@ -559,24 +556,17 @@ struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, u32 oui_type) { struct wpabuf *buf; - const u8 *end, *pos, *ie; - - pos = ies; - end = ies + ies_len; - ie = NULL; + const struct element *elem, *found = NULL; - while (end - pos > 1) { - if (2 + pos[1] > end - pos) - return NULL; - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && - WPA_GET_BE32(&pos[2]) == oui_type) { - ie = pos; + for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) { + if (elem->datalen >= 4 && + WPA_GET_BE32(elem->data) == oui_type) { + found = elem; break; } - pos += 2 + pos[1]; } - if (ie == NULL) + if (!found) return NULL; /* No specified vendor IE found */ buf = wpabuf_alloc(ies_len); @@ -587,13 +577,9 @@ struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, * There may be multiple vendor IEs in the message, so need to * concatenate their data fields. */ - while (end - pos > 1) { - if (2 + pos[1] > end - pos) - break; - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && - WPA_GET_BE32(&pos[2]) == oui_type) - wpabuf_put_data(buf, pos + 6, pos[1] - 4); - pos += 2 + pos[1]; + for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) { + if (elem->datalen >= 4 && WPA_GET_BE32(elem->data) == oui_type) + wpabuf_put_data(buf, elem->data + 4, elem->datalen - 4); } return buf; @@ -896,6 +882,41 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, } +int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth, + int sec_channel, u8 *op_class, u8 *channel) +{ + int vht = CHAN_WIDTH_UNKNOWN; + + switch (chanwidth) { + case CHAN_WIDTH_UNKNOWN: + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + case CHAN_WIDTH_40: + vht = VHT_CHANWIDTH_USE_HT; + break; + case CHAN_WIDTH_80: + vht = VHT_CHANWIDTH_80MHZ; + break; + case CHAN_WIDTH_80P80: + vht = VHT_CHANWIDTH_80P80MHZ; + break; + case CHAN_WIDTH_160: + vht = VHT_CHANWIDTH_160MHZ; + break; + } + + if (ieee80211_freq_to_channel_ext(freq, sec_channel, vht, op_class, + channel) == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_WARNING, + "Cannot determine operating class and channel (freq=%u chanwidth=%d sec_channel=%d)", + freq, chanwidth, sec_channel); + return -1; + } + + return 0; +} + + static const char *const us_op_class_cc[] = { "US", "CA", NULL }; @@ -1297,27 +1318,27 @@ const char * fc2str(u16 fc) int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, size_t ies_len) { + const struct element *elem; + 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 (!ies_buf) + return 0; - if (len > ies_len) { - wpa_hexdump(MSG_DEBUG, "Truncated IEs", - ies_buf, ies_len); - return -1; - } + for_each_element_id(elem, WLAN_EID_MULTI_BAND, ies_buf, ies_len) { + if (info->nof_ies >= MAX_NOF_MB_IES_SUPPORTED) + return 0; - 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++; - } + wpa_printf(MSG_DEBUG, "MB IE of %u bytes found", + elem->datalen + 2); + info->ies[info->nof_ies].ie = elem->data; + info->ies[info->nof_ies].ie_len = elem->datalen; + info->nof_ies++; + } - ies_len -= len; - ies_buf += len; + if (!for_each_element_completed(elem, ies_buf, ies_len)) { + wpa_hexdump(MSG_DEBUG, "Truncated IEs", ies_buf, ies_len); + return -1; } return 0; @@ -1440,22 +1461,13 @@ size_t global_op_class_size = ARRAY_SIZE(global_op_class); */ const u8 * get_ie(const u8 *ies, size_t len, u8 eid) { - const u8 *end; + const struct element *elem; if (!ies) return NULL; - end = ies + len; - - while (end - ies > 1) { - if (2 + ies[1] > end - ies) - break; - - if (ies[0] == eid) - return ies; - - ies += 2 + ies[1]; - } + for_each_element_id(elem, eid, ies, len) + return &elem->id; return NULL; } @@ -1473,22 +1485,26 @@ const u8 * get_ie(const u8 *ies, size_t len, u8 eid) */ const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext) { - const u8 *end; + const struct element *elem; if (!ies) return NULL; - end = ies + len; + for_each_element_extid(elem, ext, ies, len) + return &elem->id; + + return NULL; +} - while (end - ies > 1) { - if (2 + ies[1] > end - ies) - break; - if (ies[0] == WLAN_EID_EXTENSION && ies[1] >= 1 && - ies[2] == ext) - return ies; +const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type) +{ + const struct element *elem; - ies += 2 + ies[1]; + for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, len) { + if (elem->datalen >= 4 && + vendor_type == WPA_GET_BE32(elem->data)) + return &elem->id; } return NULL; @@ -1519,6 +1535,26 @@ size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len) } +size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value) +{ + u8 *pos = buf; + + if (len < 9) + return 0; + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = 7; /* len */ + WPA_PUT_BE24(pos, OUI_WFA); + pos += 3; + *pos++ = MULTI_AP_OUI_TYPE; + *pos++ = MULTI_AP_SUB_ELEM_TYPE; + *pos++ = 1; /* len */ + *pos++ = value; + + return pos - buf; +} + + static const struct country_op_class us_op_class[] = { { 1, 115 }, { 2, 118 }, @@ -1664,6 +1700,27 @@ const struct oper_class_map * get_oper_class(const char *country, u8 op_class) } +int oper_class_bw_to_int(const struct oper_class_map *map) +{ + switch (map->bw) { + case BW20: + return 20; + case BW40PLUS: + case BW40MINUS: + return 40; + case BW80: + return 80; + case BW80P80: + case BW160: + return 160; + case BW2160: + return 2160; + default: + return 0; + } +} + + int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep, size_t nei_rep_len) { @@ -1764,3 +1821,11 @@ int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep, return nei_pos - nei_rep; } + + +int ieee802_11_ext_capab(const u8 *ie, unsigned int capab) +{ + if (!ie || ie[1] <= capab / 8) + return 0; + return !!(ie[2 + capab / 8] & BIT(capab % 8)); +} diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index ff7e51de3dc9b..d41bd39e7a93c 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -1,6 +1,6 @@ /* * IEEE 802.11 Common routines - * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2019, 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,13 @@ #define IEEE802_11_COMMON_H #include "defs.h" +#include "ieee802_11_defs.h" + +struct element { + u8 id; + u8 datalen; + u8 data[]; +} STRUCT_PACKED; struct hostapd_hw_modes; @@ -84,6 +91,9 @@ struct ieee802_11_elems { const u8 *power_capab; const u8 *roaming_cons_sel; const u8 *password_id; + const u8 *oci; + const u8 *multi_ap; + const u8 *he_capabilities; u8 ssid_len; u8 supp_rates_len; @@ -130,6 +140,9 @@ struct ieee802_11_elems { u8 power_capab_len; u8 roaming_cons_sel_len; u8 password_id_len; + u8 oci_len; + u8 multi_ap_len; + u8 he_capabilities_len; struct mb_ies_info mb_ies; }; @@ -160,6 +173,8 @@ int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan); enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, int sec_channel, int vht, u8 *op_class, u8 *channel); +int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth, + int sec_channel, u8 *op_class, u8 *channel); int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes, u16 num_modes); enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht); @@ -186,9 +201,12 @@ extern size_t global_op_class_size; const u8 * get_ie(const u8 *ies, size_t len, u8 eid); const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext); +const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type); size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len); +size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value); + struct country_op_class { u8 country_op_class; u8 global_op_class; @@ -197,8 +215,58 @@ struct country_op_class { u8 country_to_global_op_class(const char *country, u8 op_class); const struct oper_class_map * get_oper_class(const char *country, u8 op_class); +int oper_class_bw_to_int(const struct oper_class_map *map); int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep, size_t nei_rep_len); +int ieee802_11_ext_capab(const u8 *ie, unsigned int capab); + +/* element iteration helpers */ +#define for_each_element(_elem, _data, _datalen) \ + for (_elem = (const struct element *) (_data); \ + (const u8 *) (_data) + (_datalen) - (const u8 *) _elem >= \ + (int) sizeof(*_elem) && \ + (const u8 *) (_data) + (_datalen) - (const u8 *) _elem >= \ + (int) sizeof(*_elem) + _elem->datalen; \ + _elem = (const struct element *) (_elem->data + _elem->datalen)) + +#define for_each_element_id(element, _id, data, datalen) \ + for_each_element(element, data, datalen) \ + if (element->id == (_id)) + +#define for_each_element_extid(element, extid, _data, _datalen) \ + for_each_element(element, _data, _datalen) \ + if (element->id == WLAN_EID_EXTENSION && \ + element->datalen > 0 && \ + element->data[0] == (extid)) + +#define for_each_subelement(sub, element) \ + for_each_element(sub, (element)->data, (element)->datalen) + +#define for_each_subelement_id(sub, id, element) \ + for_each_element_id(sub, id, (element)->data, (element)->datalen) + +#define for_each_subelement_extid(sub, extid, element) \ + for_each_element_extid(sub, extid, (element)->data, (element)->datalen) + +/** + * for_each_element_completed - Determine if element parsing consumed all data + * @element: Element pointer after for_each_element() or friends + * @data: Same data pointer as passed to for_each_element() or friends + * @datalen: Same data length as passed to for_each_element() or friends + * + * This function returns 1 if all the data was parsed or considered + * while walking the elements. Only use this if your for_each_element() + * loop cannot be broken out of, otherwise it always returns 0. + * + * If some data was malformed, this returns %false since the last parsed + * element will not fill the whole remaining data. + */ +static inline int for_each_element_completed(const struct element *element, + const void *data, size_t datalen) +{ + return (const u8 *) element == (const u8 *) data + datalen; +} + #endif /* IEEE802_11_COMMON_H */ diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 762e731ab022c..adaa8931093ee 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -1,6 +1,6 @@ /* * IEEE 802.11 Frame type definitions - * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> * Copyright (c) 2007-2008 Intel Corporation * * This software may be distributed under the terms of the BSD license. @@ -334,7 +334,7 @@ #define WLAN_EID_LOCATION_PARAMETERS 82 #define WLAN_EID_NONTRANSMITTED_BSSID_CAPA 83 #define WLAN_EID_SSID_LIST 84 -#define WLAN_EID_MLTIPLE_BSSID_INDEX 85 +#define WLAN_EID_MULTIPLE_BSSID_INDEX 85 #define WLAN_EID_FMS_DESCRIPTOR 86 #define WLAN_EID_FMS_REQUEST 87 #define WLAN_EID_FMS_RESPONSE 88 @@ -467,7 +467,89 @@ #define WLAN_EID_EXT_PASSWORD_IDENTIFIER 33 #define WLAN_EID_EXT_HE_CAPABILITIES 35 #define WLAN_EID_EXT_HE_OPERATION 36 - +#define WLAN_EID_EXT_HE_MU_EDCA_PARAMS 38 +#define WLAN_EID_EXT_OCV_OCI 54 + +/* Extended Capabilities field */ +#define WLAN_EXT_CAPAB_20_40_COEX 0 +#define WLAN_EXT_CAPAB_GLK 1 +#define WLAN_EXT_CAPAB_EXT_CHAN_SWITCH 2 +#define WLAN_EXT_CAPAB_GLK_GCR 3 +#define WLAN_EXT_CAPAB_PSMP 4 +/* 5 - Reserved */ +#define WLAN_EXT_CAPAB_S_PSMP 6 +#define WLAN_EXT_CAPAB_EVENT 7 +#define WLAN_EXT_CAPAB_DIAGNOSTICS 8 +#define WLAN_EXT_CAPAB_MULTICAST_DIAGNOSTICS 9 +#define WLAN_EXT_CAPAB_LOCATION_TRACKING 10 +#define WLAN_EXT_CAPAB_FMS 11 +#define WLAN_EXT_CAPAB_PROXY_ARP 12 +#define WLAN_EXT_CAPAB_COLL_INTERF_REP 13 +#define WLAN_EXT_CAPAB_CIVIC_LOCATION 14 +#define WLAN_EXT_CAPAB_GEOSPATIAL_LOCATION 15 +#define WLAN_EXT_CAPAB_TFS 16 +#define WLAN_EXT_CAPAB_WNM_SLEEP_MODE 17 +#define WLAN_EXT_CAPAB_TIM_BROADCAST 18 +#define WLAN_EXT_CAPAB_BSS_TRANSITION 19 +#define WLAN_EXT_CAPAB_QOS_TRAFFIC 20 +#define WLAN_EXT_CAPAB_AC_STA_COUNT 21 +#define WLAN_EXT_CAPAB_MULTIPLE_BSSID 22 +#define WLAN_EXT_CAPAB_TIMING_MEASUREMENT 23 +#define WLAN_EXT_CAPAB_CHANNEL_USAGE 24 +#define WLAN_EXT_CAPAB_SSID_LIST 25 +#define WLAN_EXT_CAPAB_DMS 26 +#define WLAN_EXT_CAPAB_UTF_TSF_OFFSET 27 +#define WLAN_EXT_CAPAB_TPU_BUFFER_STA 28 +#define WLAN_EXT_CAPAB_TDLS_PEER_PSM 29 +#define WLAN_EXT_CAPAB_TDLS_CHANNEL_SWITCH 30 +#define WLAN_EXT_CAPAB_INTERWORKING 31 +#define WLAN_EXT_CAPAB_QOS_MAP 32 +#define WLAN_EXT_CAPAB_EBR 33 +#define WLAN_EXT_CAPAB_SSPN_INTERFACE 34 +/* 35 - Reserved */ +#define WLAN_EXT_CAPAB_MSGCF 36 +#define WLAN_EXT_CAPAB_TDLS 37 +#define WLAN_EXT_CAPAB_TDLS_PROHIBITED 38 +#define WLAN_EXT_CAPAB_TDLS_CHANNEL_SWITCH_PROHIBITED 39 +#define WLAN_EXT_CAPAB_REJECT_UNADMITTED_FRAME 40 +#define WLAN_EXT_CAPAB_ +/* 41-43 - Service Interval Granularity */ +#define WLAN_EXT_CAPAB_IDENTIFIER_LOCATION 44 +#define WLAN_EXT_CAPAB_U_APSD_COEX 45 +#define WLAN_EXT_CAPAB_WNM_NOTIFCATION 46 +#define WLAN_EXT_CAPAB_QAB 47 +#define WLAN_EXT_CAPAB_UTF_8_SSID 48 +#define WLAN_EXT_CAPAB_QMF 49 +#define WLAN_EXT_CAPAB_QMF_RECONFIG 50 +#define WLAN_EXT_CAPAB_ROBUST_AV_STREAMING 51 +#define WLAN_EXT_CAPAB_ADVANCED_GCR 52 +#define WLAN_EXT_CAPAB_MESH_GCR 53 +#define WLAN_EXT_CAPAB_SCS 54 +#define WLAN_EXT_CAPAB_QLOAD_REPORT 55 +#define WLAN_EXT_CAPAB_ALT_EDCA 56 +#define WLAN_EXT_CAPAB_UNPROT_TXOP_NEG 57 +#define WLAN_EXT_CAPAB_PROT_TXOP_NEG 58 +/* 59 - Reserved */ +#define WLAN_EXT_CAPAB_PROT_QLOAD_REPORT 60 +#define WLAN_EXT_CAPAB_TDLS_WIDER_BW 61 +#define WLAN_EXT_CAPAB_OPMODE_NOTIF 62 +#define WLAN_EXT_CAPAB_ +/* 63-64 - Max Number of MSDUs In A-MSDU */ +#define WLAN_EXT_CAPAB_CHANNEL_SCHEDULE_MGMT 65 +#define WLAN_EXT_CAPAB_GEODB_INBAND_ENABLING_SIGNAL 66 +#define WLAN_EXT_CAPAB_NETWORK_CHANNEL_CTRL 67 +#define WLAN_EXT_CAPAB_WHITE_SPACE_MAP 68 +#define WLAN_EXT_CAPAB_CHANNEL_AVAIL_QUERY 69 +#define WLAN_EXT_CAPAB_FTM_RESPONDER 70 +#define WLAN_EXT_CAPAB_FTM_INITIATOR 71 +#define WLAN_EXT_CAPAB_FILS 72 +#define WLAN_EXT_CAPAB_EXT_SPECTRUM_MGMT 73 +#define WLAN_EXT_CAPAB_FUTURE_CHANNEL_GUIDANCE 74 +#define WLAN_EXT_CAPAB_PAD 75 +/* 76-79 - Reserved */ +#define WLAN_EXT_CAPAB_COMPLETE_NON_TX_BSSID_PROFILE 80 +#define WLAN_EXT_CAPAB_SAE_PW_ID 81 +#define WLAN_EXT_CAPAB_SAE_PW_ID_EXCLUSIVELY 82 /* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */ #define WLAN_ACTION_SPECTRUM_MGMT 0 @@ -865,10 +947,12 @@ struct ieee80211_mgmt { struct { u8 action; u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + u8 variable[]; /* OCI element */ } STRUCT_PACKED sa_query_req; struct { u8 action; /* */ u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + u8 variable[]; /* OCI element */ } STRUCT_PACKED sa_query_resp; struct { u8 action; @@ -1210,6 +1294,13 @@ struct ieee80211_ampe_ie { #define MBO_OUI_TYPE 22 #define OWE_IE_VENDOR_TYPE 0x506f9a1c #define OWE_OUI_TYPE 28 +#define MULTI_AP_OUI_TYPE 0x1B + +#define MULTI_AP_SUB_ELEM_TYPE 0x06 +#define MULTI_AP_TEAR_DOWN BIT(4) +#define MULTI_AP_FRONTHAUL_BSS BIT(5) +#define MULTI_AP_BACKHAUL_BSS BIT(6) +#define MULTI_AP_BACKHAUL_STA BIT(7) #define WMM_OUI_TYPE 2 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0 @@ -1347,13 +1438,15 @@ enum wmm_ac { #define HS20_PPS_MO_ID_PRESENT 0x02 #define HS20_ANQP_DOMAIN_ID_PRESENT 0x04 #ifndef HS20_VERSION -#define HS20_VERSION 0x10 /* Release 2 */ +#define HS20_VERSION 0x20 /* Release 3 */ #endif /* HS20_VERSION */ /* WNM-Notification WFA vendors specific subtypes */ #define HS20_WNM_SUB_REM_NEEDED 0 #define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1 -#define HS20_WNM_T_C_ACCEPTANCE 2 +#define WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT 2 +#define WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA 3 +#define HS20_WNM_T_C_ACCEPTANCE 4 #define HS20_DEAUTH_REASON_CODE_BSS 0 #define HS20_DEAUTH_REASON_CODE_ESS 1 @@ -1442,12 +1535,6 @@ enum mbo_transition_reject_reason { MBO_TRANSITION_REJECT_REASON_SERVICES = 6, }; -/* MBO v0.0_r19, 4.4: WNM-Notification vendor subelements */ -enum wfa_wnm_notif_subelem_id { - WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT = 2, - WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA = 3, -}; - /* MBO v0.0_r27, 4.3: MBO ANQP-elements */ #define MBO_ANQP_OUI_TYPE 0x12 #define MBO_ANQP_SUBTYPE_QUERY_LIST 1 @@ -1841,11 +1928,14 @@ enum beacon_report_mode { }; /* IEEE Std 802.11-2016, Table 9-88 - Beacon Request subelement IDs */ +/* IEEE P802.11-REVmd/D2.0, Table 9-106 - Optional subelement IDs for + * Beacon request */ #define WLAN_BEACON_REQUEST_SUBELEM_SSID 0 #define WLAN_BEACON_REQUEST_SUBELEM_INFO 1 /* Beacon Reporting */ #define WLAN_BEACON_REQUEST_SUBELEM_DETAIL 2 /* Reporting Detail */ #define WLAN_BEACON_REQUEST_SUBELEM_REQUEST 10 #define WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL 51 /* AP Channel Report */ +#define WLAN_BEACON_REQUEST_SUBELEM_LAST_INDICATION 164 #define WLAN_BEACON_REQUEST_SUBELEM_VENDOR 221 /* @@ -1895,9 +1985,21 @@ struct rrm_measurement_beacon_report { } STRUCT_PACKED; /* IEEE Std 802.11-2016, Table 9-112 - Beacon report Subelement IDs */ +/* IEEE P802.11-REVmd/D2.0, Table 9-130 - Optional subelement IDs for + * Beacon report */ #define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY 1 +#define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY_FRAGMENT_ID 2 +#define WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION 164 #define WLAN_BEACON_REPORT_SUBELEM_VENDOR 221 +/* IEEE P802.11-REVmd/D2.0, Table 9-232 - Data field format of the + * Reported Frame Body Fragment ID subelement */ +#define REPORTED_FRAME_BODY_SUBELEM_LEN 4 +#define REPORTED_FRAME_BODY_MORE_FRAGMENTS BIT(7) + +/* IEEE P802.11-REVmd/D2.0, 9.4.2.21.7 - Beacon report */ +#define BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN 3 + /* IEEE Std 802.11ad-2012 - Multi-band element */ struct multi_band_ie { u8 eid; /* WLAN_EID_MULTI_BAND */ @@ -2000,14 +2102,15 @@ enum nr_chan_width { }; struct ieee80211_he_capabilities { - u8 he_mac_capab_info[5]; - u8 he_phy_capab_info[9]; + u8 he_mac_capab_info[6]; + u8 he_phy_capab_info[11]; u8 he_txrx_mcs_support[12]; /* TODO: 4, 8, or 12 octets */ /* PPE Thresholds (optional) */ } STRUCT_PACKED; struct ieee80211_he_operation { - u32 he_oper_params; + u32 he_oper_params; /* HE Operation Parameters[3] and + * BSS Color Information[1] */ u8 he_mcs_nss_set[2]; u8 vht_op_info_chwidth; u8 vht_op_info_chan_center_freq_seg0_idx; @@ -2024,28 +2127,55 @@ struct ieee80211_he_operation { #define HE_PHYCAP_MU_BEAMFORMER_CAPAB ((u8) BIT(1)) /* HE Operation defines */ +/* HE Operation Parameters and BSS Color Information fields */ #define HE_OPERATION_BSS_COLOR_MASK ((u32) (BIT(0) | BIT(1) | \ BIT(2) | BIT(3) | \ BIT(4) | BIT(5))) -#define HE_OPERATION_DFLT_PE_DURATION_MASK ((u32) (BIT(6) | BIT(7) | \ - BIT(8))) -#define HE_OPERATION_DFLT_PE_DURATION_OFFSET 6 -#define HE_OPERATION_TWT_REQUIRED ((u32) BIT(9)) -#define HE_OPERATION_RTS_THRESHOLD_MASK ((u32) (BIT(10) | BIT(11) | \ - BIT(12) | BIT(13) | \ +#define HE_OPERATION_PARTIAL_BSS_COLOR ((u32) BIT(6)) +#define HE_OPERATION_BSS_COLOR_DISABLED ((u32) BIT(7)) +#define HE_OPERATION_DFLT_PE_DURATION_MASK ((u32) (BIT(8) | BIT(9) | \ + BIT(10))) +#define HE_OPERATION_DFLT_PE_DURATION_OFFSET 8 +#define HE_OPERATION_TWT_REQUIRED ((u32) BIT(11)) +#define HE_OPERATION_RTS_THRESHOLD_MASK ((u32) (BIT(12) | BIT(13) | \ BIT(14) | BIT(15) | \ BIT(16) | BIT(17) | \ - BIT(18) | BIT(19))) -#define HE_OPERATION_RTS_THRESHOLD_OFFSET 10 -#define HE_OPERATION_PARTIAL_BSS_COLOR ((u32) BIT(20)) -#define HE_OPERATION_MAX_BSSID_INDICATOR_MASK ((u32) (BIT(21) | BIT(22) | \ - BIT(23) | BIT(24) | \ - BIT(25) | BIT(26) | \ - BIT(27) | BIT(28))) -#define HE_OPERATION_MAX_BSSID_INDICATOR_OFFSET 21 -#define HE_OPERATION_TX_BSSID_INDICATOR ((u32) BIT(29)) -#define HE_OPERATION_BSS_COLOR_DISABLED ((u32) BIT(30)) -#define HE_OPERATION_BSS_DUAL_BEACON ((u32) BIT(31)) + BIT(18) | BIT(19) | \ + BIT(20) | BIT(21))) +#define HE_OPERATION_RTS_THRESHOLD_OFFSET 12 + +struct ieee80211_he_mu_edca_parameter_set { + u8 he_qos_info; + u8 he_mu_ac_be_param[3]; + u8 he_mu_ac_bk_param[3]; + u8 he_mu_ac_vi_param[3]; + u8 he_mu_ac_vo_param[3]; +} STRUCT_PACKED; + +/* HE MU AC parameter record field format */ +/* ACI/AIFSN */ +#define HE_MU_AC_PARAM_ACI_IDX 0 +#define HE_MU_AC_PARAM_AIFSN ((u8) (BIT(0) | BIT(1) | BIT(2) | BIT(3))) +#define HE_MU_AC_PARAM_ACM ((u8) BIT(4)) +#define HE_MU_AC_PARAM_ACI ((u8) (BIT(5) | BIT(6))) +/* B7: Reserved */ + +/* ECWmin/ECWmax */ +#define HE_MU_AC_PARAM_ECW_IDX 1 +#define HE_MU_AC_PARAM_ECWMIN ((u8) (BIT(0) | BIT(1) | BIT(2) | BIT(3))) +#define HE_MU_AC_PARAM_ECWMAX ((u8) (BIT(4) | BIT(5) | BIT(6) | BIT(7))) + +/* MU EDCA Timer */ +#define HE_MU_AC_PARAM_TIMER_IDX 2 + +/* HE QoS Info field */ +#define HE_QOS_INFO_EDCA_PARAM_SET_COUNT ((u8) (BIT(0) | BIT(1) | \ + BIT(2) | BIT(3))) +#define HE_QOS_INFO_Q_ACK ((u8) (BIT(4))) +#define HE_QOS_INFO_QUEUE_REQUEST ((u8) (BIT(5))) +#define HE_QOS_INFO_TXOP_REQUEST ((u8) (BIT(6))) +/* B7: Reserved if sent by an AP; More Data Ack if sent by a non-AP STA */ +#define HE_QOS_INFO_MORE_DATA_ACK ((u8) (BIT(7))) /* DPP Public Action frame identifiers - OUI_WFA */ #define DPP_OUI_TYPE 0x1A diff --git a/src/common/linux_bridge.h b/src/common/linux_bridge.h index 7b768464fb542..84386e60f7504 100644 --- a/src/common/linux_bridge.h +++ b/src/common/linux_bridge.h @@ -9,6 +9,21 @@ #ifndef LINUX_BRIDGE_H #define LINUX_BRIDGE_H +/* This ioctl is defined in linux/sockios.h */ + +#ifndef SIOCBRADDBR +#define SIOCBRADDBR 0x89a0 +#endif +#ifndef SIOCBRDELBR +#define SIOCBRDELBR 0x89a1 +#endif +#ifndef SIOCBRADDIF +#define SIOCBRADDIF 0x89a2 +#endif +#ifndef SIOCBRDELIF +#define SIOCBRDELIF 0x89a3 +#endif + /* This interface is defined in linux/if_bridge.h */ #define BRCTL_GET_VERSION 0 diff --git a/src/common/ocv.c b/src/common/ocv.c new file mode 100644 index 0000000000000..06badfbfb4549 --- /dev/null +++ b/src/common/ocv.c @@ -0,0 +1,172 @@ +/* + * Operating Channel Validation (OCV) + * Copyright (c) 2018, Mathy Vanhoef + * + * 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 "drivers/driver.h" +#include "common/ieee802_11_common.h" +#include "ocv.h" + +/** + * Caller of OCV functionality may use various debug output functions, so store + * the error here and let the caller use an appropriate debug output function. + */ +char ocv_errorstr[256]; + + +int ocv_derive_all_parameters(struct oci_info *oci) +{ + const struct oper_class_map *op_class_map; + + oci->freq = ieee80211_chan_to_freq(NULL, oci->op_class, oci->channel); + if (oci->freq < 0) { + wpa_printf(MSG_INFO, + "Error interpreting OCI: unrecognized opclass/channel pair (%d/%d)", + oci->op_class, oci->channel); + return -1; + } + + op_class_map = get_oper_class(NULL, oci->op_class); + if (!op_class_map) { + wpa_printf(MSG_INFO, + "Error interpreting OCI: Unrecognized opclass (%d)", + oci->op_class); + return -1; + } + + oci->chanwidth = oper_class_bw_to_int(op_class_map); + oci->sec_channel = 0; + if (op_class_map->bw == BW40PLUS) + oci->sec_channel = 1; + else if (op_class_map->bw == BW40MINUS) + oci->sec_channel = -1; + + return 0; +} + + +int ocv_insert_oci(struct wpa_channel_info *ci, u8 **argpos) +{ + u8 op_class, channel; + u8 *pos = *argpos; + + if (ieee80211_chaninfo_to_channel(ci->frequency, ci->chanwidth, + ci->sec_channel, + &op_class, &channel) < 0) { + wpa_printf(MSG_WARNING, + "Cannot determine operating class and channel for OCI element"); + return -1; + } + + *pos++ = op_class; + *pos++ = channel; + *pos++ = ci->seg1_idx; + + *argpos = pos; + return 0; +} + + +int ocv_insert_oci_kde(struct wpa_channel_info *ci, u8 **argpos) +{ + u8 *pos = *argpos; + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = RSN_SELECTOR_LEN + 3; + RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_OCI); + pos += RSN_SELECTOR_LEN; + + *argpos = pos; + return ocv_insert_oci(ci, argpos); +} + + +int ocv_insert_extended_oci(struct wpa_channel_info *ci, u8 *pos) +{ + *pos++ = WLAN_EID_EXTENSION; + *pos++ = 1 + OCV_OCI_LEN; + *pos++ = WLAN_EID_EXT_OCV_OCI; + return ocv_insert_oci(ci, &pos); +} + + +int ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len, + struct wpa_channel_info *ci, int tx_chanwidth, + int tx_seg1_idx) +{ + struct oci_info oci; + + if (!oci_ie) { + os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), + "OCV failed: did not receive mandatory OCI"); + return -1; + } + + if (oci_ie_len != 3) { + os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), + "OCV failed: received OCI of unexpected length (%d)", + (int) oci_ie_len); + return -1; + } + + os_memset(&oci, 0, sizeof(oci)); + oci.op_class = oci_ie[0]; + oci.channel = oci_ie[1]; + oci.seg1_idx = oci_ie[2]; + if (ocv_derive_all_parameters(&oci) != 0) { + os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), + "OCV failed: unable to interpret received OCI"); + return -1; + } + + /* Primary frequency used to send frames to STA must match the STA's */ + if ((int) ci->frequency != oci.freq) { + os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), + "OCV failed: primary channel mismatch in received OCI (we use %d but receiver is using %d)", + ci->frequency, oci.freq); + return -1; + } + + /* We shouldn't transmit with a higher bandwidth than the STA supports + */ + if (tx_chanwidth > oci.chanwidth) { + os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), + "OCV failed: channel bandwidth mismatch in received OCI (we use %d but receiver only supports %d)", + tx_chanwidth, oci.chanwidth); + return -1; + } + + /* + * Secondary channel only needs be checked for 40 MHz in the 2.4 GHz + * band. In the 5 GHz band it's verified through the primary frequency. + * Note that the field ci->sec_channel is only filled in when we use + * 40 MHz. + */ + if (tx_chanwidth == 40 && ci->frequency < 2500 && + ci->sec_channel != oci.sec_channel) { + os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), + "OCV failed: secondary channel mismatch in received OCI (we use %d but receiver is using %d)", + ci->sec_channel, oci.sec_channel); + return -1; + } + + /* + * When using a 160 or 80+80 MHz channel to transmit, verify that we use + * the same segments as the receiver by comparing frequency segment 1. + */ + if ((ci->chanwidth == CHAN_WIDTH_160 || + ci->chanwidth == CHAN_WIDTH_80P80) && + tx_seg1_idx != oci.seg1_idx) { + os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), + "OCV failed: frequency segment 1 mismatch in received OCI (we use %d but receiver is using %d)", + tx_seg1_idx, oci.seg1_idx); + return -1; + } + + return 0; +} diff --git a/src/common/ocv.h b/src/common/ocv.h new file mode 100644 index 0000000000000..6379d9d06c9ac --- /dev/null +++ b/src/common/ocv.h @@ -0,0 +1,40 @@ +/* + * Operating Channel Validation (OCV) + * Copyright (c) 2018, Mathy Vanhoef + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef OCV_H +#define OCV_H + +struct wpa_channel_info; + +struct oci_info { + /* Values in the OCI element */ + u8 op_class; + u8 channel; + u8 seg1_idx; + + /* Derived values for easier verification */ + int freq; + int sec_channel; + int chanwidth; +}; + +#define OCV_OCI_LEN 3 +#define OCV_OCI_EXTENDED_LEN (3 + OCV_OCI_LEN) +#define OCV_OCI_KDE_LEN (2 + RSN_SELECTOR_LEN + OCV_OCI_LEN) + +extern char ocv_errorstr[256]; + +int ocv_derive_all_parameters(struct oci_info *oci); +int ocv_insert_oci(struct wpa_channel_info *ci, u8 **argpos); +int ocv_insert_oci_kde(struct wpa_channel_info *ci, u8 **argpos); +int ocv_insert_extended_oci(struct wpa_channel_info *ci, u8 *pos); +int ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len, + struct wpa_channel_info *ci, int tx_chanwidth, + int tx_seg1_idx); + +#endif /* OCV_H */ diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h index 7c75d0804553d..c34a3bc1f3e41 100644 --- a/src/common/qca-vendor.h +++ b/src/common/qca-vendor.h @@ -42,8 +42,12 @@ enum qca_radiotap_vendor_ids { * * @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: Recommendation of frequency * ranges to avoid to reduce issues due to interference or internal - * co-existence information in the driver. The event data structure is - * defined in struct qca_avoid_freq_list. + * co-existence information in the driver. These frequencies aim to + * minimize the traffic but not to totally avoid the traffic. That said + * for a P2P use case, these frequencies are allowed for the P2P + * discovery/negotiation but avoid the group to get formed on these + * frequencies. The event data structure is defined in + * struct qca_avoid_freq_list. * * @QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: Command to check driver support * for DFS offloading. @@ -499,6 +503,27 @@ enum qca_radiotap_vendor_ids { * * Based on the config provided, FW will boost the weight and prioritize * the traffic for that subsystem (WLAN/BT/Zigbee). + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS: This command is used to query + * the supported AKM suite selectorss from the driver. It returns the list + * of supported AKMs in the attribute NL80211_ATTR_AKM_SUITES. + * @QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE: This command is used to get firmware + * state from the driver. It returns the firmware state in the attribute + * QCA_WLAN_VENDOR_ATTR_FW_STATE. + * @QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH: This vendor subcommand + * is used by the driver to flush per-peer cached statistics to user space + * application. This interface is used as an event from the driver to + * user space application. Attributes for this event are specified in + * enum qca_wlan_vendor_attr_peer_stats_cache_params. + * QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA attribute is expected to be + * sent in the event. + * @QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG: This sub command is used to + * improve the success rate of Zigbee joining network. + * Due to PTA master limitation, Zigbee joining network success rate is + * low while WLAN is working. The WLAN driver needs to configure some + * parameters including Zigbee state and specific WLAN periods to enhance + * PTA master. All these parameters are delivered by the attributes + * defined in enum qca_mpta_helper_vendor_attr. */ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, @@ -663,6 +688,10 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG = 173, QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT = 174, QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG = 175, + QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS = 176, + QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE = 177, + QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH = 178, + QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG = 179, }; enum qca_wlan_vendor_attr { @@ -843,6 +872,18 @@ enum qca_wlan_vendor_attr { * to report the corresponding antenna index to the chain RSSI value */ QCA_WLAN_VENDOR_ATTR_ANTENNA_INFO = 40, + /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command to report + * the specific antenna EVM value (unsigned 32 bit value). With a + * determinate group of antennas, the driver specifies the EVM value + * for each antenna ID, and application extract them in user space. + */ + QCA_WLAN_VENDOR_ATTR_CHAIN_EVM = 41, + /* + * Used in QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE command to report + * wlan firmware current state. FW state is an unsigned 8 bit value, + * one of the values in enum qca_wlan_vendor_attr_fw_state. + */ + QCA_WLAN_VENDOR_ATTR_FW_STATE = 42, /* keep last */ QCA_WLAN_VENDOR_ATTR_AFTER_LAST, @@ -854,6 +895,49 @@ enum qca_roaming_policy { QCA_ROAMING_ALLOWED_WITHIN_ESS, }; +/** + * enum qca_roam_reason - Represents the reason codes for roaming. Used by + * QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON. + * + * @QCA_ROAM_REASON_UNKNOWN: Any reason that do not classify under the below + * reasons. + * + * @QCA_ROAM_REASON_PER: Roam triggered when packet error rates (PER) breached + * the configured threshold. + * + * @QCA_ROAM_REASON_BEACON_MISS: Roam triggered due to the continuous configured + * beacon misses from the then connected AP. + * + * @QCA_ROAM_REASON_POOR_RSSI: Roam triggered due to the poor RSSI reported + * by the connected AP. + * + * @QCA_ROAM_REASON_BETTER_RSSI: Roam triggered for finding a BSS with a better + * RSSI than the connected BSS. Here the RSSI of the current BSS is not poor. + * + * @QCA_ROAM_REASON_CONGESTION: Roam triggered considering the connected channel + * or environment being very noisy or congested. + * + * @QCA_ROAM_REASON_EXPLICIT_REQUEST: Roam triggered due to an explicit request + * from the user (user space). + * + * @QCA_ROAM_REASON_BTM: Roam triggered due to BTM Request frame received from + * the connected AP. + * + * @QCA_ROAM_REASON_BSS_LOAD: Roam triggered due to the channel utilization + * breaching out the configured threshold. + */ +enum qca_roam_reason { + QCA_ROAM_REASON_UNKNOWN, + QCA_ROAM_REASON_PER, + QCA_ROAM_REASON_BEACON_MISS, + QCA_ROAM_REASON_POOR_RSSI, + QCA_ROAM_REASON_BETTER_RSSI, + QCA_ROAM_REASON_CONGESTION, + QCA_ROAM_REASON_USER_TRIGGER, + QCA_ROAM_REASON_BTM, + QCA_ROAM_REASON_BSS_LOAD, +}; + enum qca_wlan_vendor_attr_roam_auth { QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID, @@ -896,6 +980,11 @@ enum qca_wlan_vendor_attr_roam_auth { * doing subsequent ERP based connections in the same realm. */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM = 13, + /* A 16-bit unsigned value representing the reasons for the roaming. + * Defined by enum qca_roam_reason. + */ + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON = 14, + /* keep last */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX = @@ -994,6 +1083,7 @@ enum qca_wlan_vendor_acs_hw_mode { * only OCE STA-CFON functionalities. * @QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY: Device supports self * managed regulatory. + * @QCA_WLAN_VENDOR_FEATURE_TWT: Device supports TWT (Target Wake Time). * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits */ enum qca_wlan_vendor_features { @@ -1005,6 +1095,7 @@ enum qca_wlan_vendor_features { QCA_WLAN_VENDOR_FEATURE_OCE_AP = 5, QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON = 6, QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY = 7, + QCA_WLAN_VENDOR_FEATURE_TWT = 8, NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */ }; @@ -2438,6 +2529,18 @@ enum qca_wlan_vendor_attr_dmg_rf_sector_type { }; /** + * enum qca_wlan_vendor_attr_fw_state - State of firmware + * + * @QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR: FW is in bad state + * @QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE: FW is active + */ +enum qca_wlan_vendor_attr_fw_state { + QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR, + QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE, + QCA_WLAN_VENDOR_ATTR_FW_STATE_MAX +}; + +/** * BRP antenna limit mode * * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE: Disable BRP force @@ -3181,6 +3284,8 @@ enum qca_wlan_vendor_attr_roaming_config_params { QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS = 18, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID = 19, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID = 20, + /* Flag attribute indicates this BSSID blacklist as a hint */ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT = 21, /* keep last */ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST, @@ -4433,6 +4538,27 @@ enum qca_wlan_vendor_attr_spectral_cap { * qca_wlan_vendor_spectral_scan_cap_hw_gen. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN = 5, + /* Spectral bin scaling formula ID. u16 attribute. + * It uses values defined in enum + * qca_wlan_vendor_spectral_scan_cap_formula_id. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_FORMULA_ID = 6, + /* Spectral bin scaling param - low level offset. + * s16 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_LOW_LEVEL_OFFSET = 7, + /* Spectral bin scaling param - high level offset. + * s16 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HIGH_LEVEL_OFFSET = 8, + /* Spectral bin scaling param - RSSI threshold. + * s16 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_RSSI_THR = 9, + /* Spectral bin scaling param - default AGC max gain. + * u8 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_DEFAULT_AGC_MAX_GAIN = 10, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_MAX = @@ -4578,6 +4704,20 @@ enum qca_wlan_vendor_attr_flush_pending { }; /** + * qca_wlan_vendor_spectral_scan_cap_formula_id: Attribute values for + * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_FORMULA_ID in the vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO. This represents the + * Spectral bin scaling formula ID. + * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_NO_SCALING: No scaling + * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_AGC_GAIN_RSSI_CORR_BASED: AGC gain + * and RSSI threshold based formula. + */ +enum qca_wlan_vendor_spectral_scan_cap_formula_id { + QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_NO_SCALING = 0, + QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_AGC_GAIN_RSSI_CORR_BASED = 1, +}; + +/** * enum qca_wlan_vendor_attr_rropavail_info - Specifies whether Representative * RF Operating Parameter (RROP) information is available, and if so, at which * point in the application-driver interaction sequence it can be retrieved by @@ -4836,6 +4976,10 @@ enum qca_wlan_vendor_attr_offloaded_packets { QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR, /* Unsigned 32-bit value, in milli seconds */ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD, + /* This optional unsigned 16-bit attribute is used for specifying + * ethernet protocol type. If not specified ethertype defaults to IPv4. + */ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_ETHER_PROTO_TYPE, /* keep last */ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST, @@ -5457,6 +5601,54 @@ enum qca_wlan_he_om_ctrl_ch_bw { QCA_WLAN_HE_OM_CTRL_BW_160M = 3, }; +/** + * enum qca_wlan_vendor_attr_he_omi_tx: Represents attributes for + * HE operating mode control transmit request. These attributes are + * sent as part of QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX and + * QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. + * + * @QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS: Mandatory 8-bit unsigned value + * indicates the maximum number of spatial streams, NSS, that the STA + * supports in reception for PPDU bandwidths less than or equal to 80 MHz + * and is set to NSS - 1. + * + * @QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW: Mandatory 8-bit unsigned value + * indicates the operating channel width supported by the STA for both + * reception and transmission. Uses enum qca_wlan_he_om_ctrl_ch_bw values. + * + * @QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE: Mandatory 8-bit unsigned value + * indicates the all trigger based UL MU operations by the STA. + * 0 - UL MU operations are enabled by the STA. + * 1 - All triggered UL MU transmissions are suspended by the STA. + * + * @QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS: Mandatory 8-bit unsigned value + * indicates the maximum number of space-time streams, NSTS, that + * the STA supports in transmission and is set to NSTS - 1. + * + * @QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE: 8-bit unsigned value + * combined with the UL MU Disable subfield and the recipient's setting + * of the OM Control UL MU Data Disable RX Support subfield in the HE MAC + * capabilities to determine which HE TB PPDUs are possible by the + * STA to transmit. + * 0 - UL MU data operations are enabled by the STA. + * 1 - Determine which HE TB PPDU types are allowed by the STA if UL MU disable + * bit is not set, else UL MU Tx is suspended. + * + */ +enum qca_wlan_vendor_attr_he_omi_tx { + QCA_WLAN_VENDOR_ATTR_HE_OMI_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS = 1, + QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW = 2, + QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE = 3, + QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS = 4, + QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE = 5, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_HE_OMI_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_HE_OMI_MAX = + QCA_WLAN_VENDOR_ATTR_HE_OMI_AFTER_LAST - 1, +}; + /* Attributes for data used by * QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION */ @@ -5703,6 +5895,43 @@ enum qca_wlan_vendor_attr_wifi_test_config { */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_ACTION_TX_TB_PPDU = 32, + /* Nested attribute to indicate HE operating mode control field + * transmission. It contains operating mode control field Nss, + * channel bandwidth, Tx Nsts and UL MU disable attributes. + * These nested attributes are used to send HE operating mode control + * with configured values. + * Uses the enum qca_wlan_vendor_attr_he_omi_tx attributes. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX = 33, + + /* 8-bit unsigned value to configure +HTC_HE support to indicate the + * support for the reception of a frame that carries an HE variant + * HT Control field. + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_HTC_HE_SUPP = 34, + + /* 8-bit unsigned value to configure VHT support in 2.4G band. + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_2G_VHT = 35, + + /* 8-bit unsigned value to configure HE testbed defaults. + * This attribute is used to configure the testbed device. + * 1-set the device HE capabilities to testbed defaults. + * 0-reset the device HE capabilities to supported config. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SET_HE_TESTBED_DEFAULTS = 36, + + /* 8-bit unsigned value to configure TWT request support. + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TWT_REQ_SUPPORT = 37, + /* keep last */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX = @@ -6185,13 +6414,35 @@ enum qca_coex_config_profiles { QCA_WIFI_SAP_CLASS_3_MGMT = 7, QCA_WIFI_SAP_DATA = 8, QCA_WIFI_SAP_ALL = 9, + QCA_WIFI_CASE_MAX = 31, /* 32 - 63 corresponds to BT */ QCA_BT_A2DP = 32, QCA_BT_BLE = 33, QCA_BT_SCO = 34, + QCA_BT_CASE_MAX = 63, /* 64 - 95 corresponds to Zigbee */ QCA_ZB_LOW = 64, - QCA_ZB_HIGH = 65 + QCA_ZB_HIGH = 65, + QCA_ZB_CASE_MAX = 95, + /* 0xff is default value if the u8 profile value is not set. */ + QCA_COEX_CONFIG_PROFILE_DEFAULT_VALUE = 255 +}; + +/** + * enum qca_vendor_attr_coex_config_types - Coex configurations types. + * This enum defines the valid set of values of coex configuration types. These + * values may used by attribute + * %QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE. + * + * @QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET: Reset all the + * weights to default values. + * @QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START: Start to config + * weights with configurability value. + */ +enum qca_vendor_attr_coex_config_types { + QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET = 1, + QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START = 2, }; /** @@ -6223,6 +6474,80 @@ enum qca_vendor_attr_coex_config { }; /** + * enum qca_vendor_attr_coex_config_three_way - Specifies vendor coex config + * attributes + * Attributes for data used by QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG + * + * QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE: u32 attribute. + * Indicate config type. + * The config types are 32-bit values from qca_vendor_attr_coex_config_types + * + * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1: u32 attribute. + * Indicate the Priority 1 profiles. + * The profiles are 8-bit values from enum qca_coex_config_profiles. + * In same priority level, maximum to 4 profiles can be set here. + * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2: u32 attribute. + * Indicate the Priority 2 profiles. + * The profiles are 8-bit values from enum qca_coex_config_profiles. + * In same priority level, maximum to 4 profiles can be set here. + * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3: u32 attribute. + * Indicate the Priority 3 profiles. + * The profiles are 8-bit values from enum qca_coex_config_profiles. + * In same priority level, maximum to 4 profiles can be set here. + * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4: u32 attribute. + * Indicate the Priority 4 profiles. + * The profiles are 8-bit values from enum qca_coex_config_profiles. + * In same priority level, maximum to 4 profiles can be set here. + * NOTE: + * Limitations for QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_x priority + * arrangement: + * 1: In the same u32 attribute (priority x), the profiles enum values own + * same priority level. + * 2: 0xff is default value if the u8 profile value is not set. + * 3: max to 4 rules/profiles in same priority level. + * 4: max to 4 priority level (priority 1 - priority 4) + * 5: one priority level only supports one scenario from WLAN/BT/ZB, + * hybrid rules not support. + * 6: if WMI_COEX_CONFIG_THREE_WAY_COEX_RESET called, priority x will + * remain blank to reset all parameters. + * For example: + * + * If the attributes as follow: + * priority 1: + * ------------------------------------ + * | 0xff | 0 | 1 | 2 | + * ------------------------------------ + * priority 2: + * ------------------------------------- + * | 0xff | 0xff | 0xff | 32 | + * ------------------------------------- + * priority 3: + * ------------------------------------- + * | 0xff | 0xff | 0xff | 65 | + * ------------------------------------- + * then it means: + * 1: WIFI_STA_DISCOVERY, WIFI_STA_CLASS_3_MGMT and WIFI_STA_CONNECTION + * owns same priority level. + * 2: WIFI_STA_DISCOVERY, WIFI_STA_CLASS_3_MGMT and WIFI_STA_CONNECTION + * has priority over BT_A2DP and ZB_HIGH. + * 3: BT_A2DP has priority over ZB_HIGH. + */ + +enum qca_vendor_attr_coex_config_three_way { + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_INVALID = 0, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE = 1, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1 = 2, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2 = 3, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3 = 4, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4 = 5, + + /* Keep last */ + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_AFTER_LAST, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_MAX = + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_AFTER_LAST - 1, +}; + +/** * enum qca_wlan_vendor_attr_link_properties - Represent the link properties. * * @QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR: MAC address of the peer @@ -6244,4 +6569,144 @@ enum qca_wlan_vendor_attr_link_properties { QCA_VENDOR_ATTR_LINK_PROPERTIES_AFTER_LAST - 1, }; +/** + * enum qca_vendor_attr_peer_stats_cache_type - Represents peer stats cache type + * This enum defines the valid set of values of peer stats cache types. These + * values are used by attribute + * %QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_TX_RATE_STATS: Represents peer TX rate statistics + * @QCA_WLAN_VENDOR_ATTR_PEER_RX_RATE_STATS: Represents peer RX rate statistics + * @QCA_WLAN_VENDOR_ATTR_PEER_TX_SOJOURN_STATS: Represents peer TX sojourn + * statistics + */ +enum qca_vendor_attr_peer_stats_cache_type { + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE_INVALID = 0, + + QCA_WLAN_VENDOR_ATTR_PEER_TX_RATE_STATS, + QCA_WLAN_VENDOR_ATTR_PEER_RX_RATE_STATS, + QCA_WLAN_VENDOR_ATTR_PEER_TX_SOJOURN_STATS, +}; + +/** + * enum qca_wlan_vendor_attr_peer_stats_cache_params - This enum defines + * attributes required for QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH + * Information in these attributes is used to flush peer rate statistics from + * the driver to user application. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE: Unsigned 32-bit attribute + * Indicate peer statistics cache type. + * The statistics types are 32-bit values from + * enum qca_vendor_attr_peer_stats_cache_type. + * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC: Unsigned 8-bit array + * of size 6 octets, representing the peer MAC address. + * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA: Opaque data attribute + * containing buffer of statistics to send to application layer entity. + * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE: Unsigned 64-bit attribute + * representing a cookie for peer unique session. + */ +enum qca_wlan_vendor_attr_peer_stats_cache_params { + QCA_WLAN_VENDOR_ATTR_PEER_STATS_INVALID = 0, + + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE = 1, + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC = 2, + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA = 3, + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE = 4, + + /* Keep last */ + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST, + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_MAX = + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST - 1 +}; + +/** + * enum qca_mpta_helper_attr_zigbee_state - Current Zigbee state + * This enum defines all the possible states of Zigbee, which can be + * delivered in the QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE attribute. + * + * @ZIGBEE_IDLE: Zigbee in idle state + * @ZIGBEE_FORM_NETWORK: Zigbee forming network + * @ZIGBEE_WAIT_JOIN: Zigbee waiting for joining network + * @ZIGBEE_JOIN: Zigbee joining network + * @ZIGBEE_NETWORK_UP: Zigbee network is up + * @ZIGBEE_HMI: Zigbee in HMI mode + */ +enum qca_mpta_helper_attr_zigbee_state { + ZIGBEE_IDLE = 0, + ZIGBEE_FORM_NETWORK = 1, + ZIGBEE_WAIT_JOIN = 2, + ZIGBEE_JOIN = 3, + ZIGBEE_NETWORK_UP = 4, + ZIGBEE_HMI = 5, +}; + +/* + * enum qca_mpta_helper_vendor_attr - Attributes used in vendor sub-command + * QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG. + */ +enum qca_mpta_helper_vendor_attr { + QCA_MPTA_HELPER_VENDOR_ATTR_INVALID = 0, + /* Optional attribute used to update Zigbee state. + * enum qca_mpta_helper_attr_zigbee_state. + * NLA_U32 attribute. + */ + QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE = 1, + /* Optional attribute used to configure WLAN duration for Shape-OCS + * during interrupt. + * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION. + * Value range 0 ~ 300 (ms). + * NLA_U32 attribute. + */ + QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION = 2, + /* Optional attribute used to configure non-WLAN duration for Shape-OCS + * during interrupt. + * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION. + * Value range 0 ~ 300 (ms). + * NLA_U32 attribute. + */ + QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION = 3, + /* Optional attribute used to configure WLAN duration for Shape-OCS + * monitor period. + * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION. + * Value range 0 ~ 300 (ms) + * NLA_U32 attribute + */ + QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION = 4, + /* Optional attribute used to configure non-WLAN duration for Shape-OCS + * monitor period. + * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION. + * Value range 0 ~ 300 (ms) + * NLA_U32 attribute + */ + QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION = 5, + /* Optional attribute used to configure OCS interrupt duration. + * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION. + * Value range 1000 ~ 20000 (ms) + * NLA_U32 attribute + */ + QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION = 6, + /* Optional attribute used to configure OCS monitor duration. + * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION. + * Value range 1000 ~ 20000 (ms) + * NLA_U32 attribute + */ + QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION = 7, + /* Optional attribute used to notify WLAN firmware the current Zigbee + * channel. + * Value range 11 ~ 26 + * NLA_U32 attribute + */ + QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_CHAN = 8, + /* Optional attribute used to configure WLAN mute duration. + * Value range 0 ~ 400 (ms) + * NLA_U32 attribute + */ + QCA_MPTA_HELPER_VENDOR_ATTR_WLAN_MUTE_DURATION = 9, + + /* keep last */ + QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST, + QCA_MPTA_HELPER_VENDOR_ATTR_MAX = + QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST - 1 +}; + #endif /* QCA_VENDOR_H */ diff --git a/src/common/sae.c b/src/common/sae.c index 981e788dc7518..5a50294a6dc8f 100644 --- a/src/common/sae.c +++ b/src/common/sae.c @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "utils/const_time.h" #include "crypto/crypto.h" #include "crypto/sha256.h" #include "crypto/random.h" @@ -17,10 +18,33 @@ #include "sae.h" +static int sae_suitable_group(int group) +{ +#ifdef CONFIG_TESTING_OPTIONS + /* Allow all groups for testing purposes in non-production builds. */ + return 1; +#else /* CONFIG_TESTING_OPTIONS */ + /* Enforce REVmd rules on which SAE groups are suitable for production + * purposes: FFC groups whose prime is >= 3072 bits and ECC groups + * defined over a prime field whose prime is >= 256 bits. Furthermore, + * ECC groups defined over a characteristic 2 finite field and ECC + * groups with a co-factor greater than 1 are not suitable. */ + return group == 19 || group == 20 || group == 21 || + group == 28 || group == 29 || group == 30 || + group == 15 || group == 16 || group == 17 || group == 18; +#endif /* CONFIG_TESTING_OPTIONS */ +} + + int sae_set_group(struct sae_data *sae, int group) { struct sae_temporary_data *tmp; + if (!sae_suitable_group(group)) { + wpa_printf(MSG_DEBUG, "SAE: Reject unsuitable group %d", group); + return -1; + } + sae_clear_data(sae); tmp = sae->tmp = os_zalloc(sizeof(*tmp)); if (tmp == NULL) @@ -208,12 +232,14 @@ get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits, 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 u8 *qr, const u8 *qnr, const struct crypto_bignum *y_sqr) { - struct crypto_bignum *r, *num; + struct crypto_bignum *r, *num, *qr_or_qnr = NULL; int r_odd, check, res = -1; + u8 qr_or_qnr_bin[SAE_MAX_ECC_PRIME_LEN]; + size_t prime_len = sae->tmp->prime_len; + unsigned int mask; /* * Use the blinding technique to mask y_sqr while determining @@ -224,7 +250,7 @@ static int is_quadratic_residue_blind(struct sae_data *sae, * 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); + r = get_rand_1_to_p_1(prime, prime_len, bits, &r_odd); if (!r) return -1; @@ -234,50 +260,51 @@ static int is_quadratic_residue_blind(struct sae_data *sae, 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; - } + /* + * Need to minimize differences in handling different cases, so try to + * avoid branches and timing differences. + * + * If r_odd: + * num = (num * qr) module p + * LGR(num, p) = 1 ==> quadratic residue + * else: + * num = (num * qnr) module p + * LGR(num, p) = -1 ==> quadratic residue + */ + mask = const_time_is_zero(r_odd); + const_time_select_bin(mask, qnr, qr, prime_len, qr_or_qnr_bin); + qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, prime_len); + if (!qr_or_qnr || + crypto_bignum_mulmod(num, qr_or_qnr, sae->tmp->prime, num) < 0) + goto fail; + /* r_odd is 0 or 1; branchless version of check = r_odd ? 1 : -1, */ + check = const_time_select_int(mask, -1, 1); res = crypto_bignum_legendre(num, sae->tmp->prime); if (res == -2) { res = -1; goto fail; } - res = res == check; + /* branchless version of res = res == check + * (res is -1, 0, or 1; check is -1 or 1) */ + mask = const_time_eq(res, check); + res = const_time_select_int(mask, 1, 0); fail: crypto_bignum_deinit(num, 1); crypto_bignum_deinit(r, 1); + crypto_bignum_deinit(qr_or_qnr, 1); return res; } static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, - const u8 *prime, - const struct crypto_bignum *qr, - const struct crypto_bignum *qnr, - struct crypto_bignum **ret_x_cand) + const u8 *prime, const u8 *qr, const u8 *qnr, + u8 *pwd_value) { - u8 pwd_value[SAE_MAX_ECC_PRIME_LEN]; struct crypto_bignum *y_sqr, *x_cand; int res; size_t bits; - *ret_x_cand = NULL; - wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ @@ -286,7 +313,7 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, prime, sae->tmp->prime_len, pwd_value, bits) < 0) return -1; if (bits % 8) - buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); + buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8); wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, sae->tmp->prime_len); @@ -297,31 +324,27 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, 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); + crypto_bignum_deinit(x_cand, 1); + if (!y_sqr) return -1; - } 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; + return res; } +/* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided + * pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, struct crypto_bignum *pwe) { u8 pwd_value[SAE_MAX_PRIME_LEN]; size_t bits = sae->tmp->prime_len * 8; u8 exp[1]; - struct crypto_bignum *a, *b; - int res; + struct crypto_bignum *a, *b = NULL; + int res, is_val; + u8 pwd_value_valid; wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); @@ -333,16 +356,29 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, sae->tmp->prime_len); - if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0) - { - wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p"); - return 0; - } + /* Check whether pwd-value < p */ + res = const_time_memcmp(pwd_value, sae->tmp->dh->prime, + sae->tmp->prime_len); + /* pwd-value >= p is invalid, so res is < 0 for the valid cases and + * the negative sign can be used to fill the mask for constant time + * selection */ + pwd_value_valid = const_time_fill_msb(res); + + /* If pwd-value >= p, force pwd-value to be < p and perform the + * calculations anyway to hide timing difference. The derived PWE will + * be ignored in that case. */ + pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0); /* PWE = pwd-value^((p-1)/r) modulo p */ + res = -1; a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); + if (!a) + goto fail; + /* This is an optimization based on the used group that does not depend + * on the password in any way, so it is fine to use separate branches + * for this step without constant time operations. */ if (sae->tmp->dh->safe_prime) { /* * r = (p-1)/2 for the group used here, so this becomes: @@ -356,33 +392,34 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, b = crypto_bignum_init_set(exp, sizeof(exp)); if (b == NULL || crypto_bignum_sub(sae->tmp->prime, b, b) < 0 || - crypto_bignum_div(b, sae->tmp->order, b) < 0) { - crypto_bignum_deinit(b, 0); - b = NULL; - } + crypto_bignum_div(b, sae->tmp->order, b) < 0) + goto fail; } - if (a == NULL || b == NULL) - res = -1; - else - res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe); - - crypto_bignum_deinit(a, 0); - crypto_bignum_deinit(b, 0); + if (!b) + goto fail; - if (res < 0) { - wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE"); - return -1; - } + res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe); + if (res < 0) + goto fail; - /* if (PWE > 1) --> found */ - if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) { - wpa_printf(MSG_DEBUG, "SAE: PWE <= 1"); - return 0; - } + /* There were no fatal errors in calculations, so determine the return + * value using constant time operations. We get here for number of + * invalid cases which are cleared here after having performed all the + * computation. PWE is valid if pwd-value was less than prime and + * PWE > 1. Start with pwd-value check first and then use constant time + * operations to clear res to 0 if PWE is 0 or 1. + */ + res = const_time_select_u8(pwd_value_valid, 1, 0); + is_val = crypto_bignum_is_zero(pwe); + res = const_time_select_u8(const_time_is_zero(is_val), res, 0); + is_val = crypto_bignum_is_one(pwe); + res = const_time_select_u8(const_time_is_zero(is_val), res, 0); - wpa_printf(MSG_DEBUG, "SAE: PWE found"); - return 1; +fail: + crypto_bignum_deinit(a, 1); + crypto_bignum_deinit(b, 1); + return res; } @@ -431,25 +468,32 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, const u8 *addr[3]; size_t len[3]; size_t num_elem; - u8 dummy_password[32]; - size_t dummy_password_len; + u8 *dummy_password, *tmp_password; int pwd_seed_odd = 0; u8 prime[SAE_MAX_ECC_PRIME_LEN]; size_t prime_len; - struct crypto_bignum *x = NULL, *qr, *qnr; + struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL; + u8 x_bin[SAE_MAX_ECC_PRIME_LEN]; + u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN]; + u8 qr_bin[SAE_MAX_ECC_PRIME_LEN]; + u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN]; size_t bits; - int res; + int res = -1; + u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* + * mask */ - 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; + os_memset(x_bin, 0, sizeof(x_bin)); + + dummy_password = os_malloc(password_len); + tmp_password = os_malloc(password_len); + if (!dummy_password || !tmp_password || + random_get_bytes(dummy_password, password_len) < 0) + goto fail; prime_len = sae->tmp->prime_len; if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), prime_len) < 0) - return -1; + goto fail; bits = crypto_ec_prime_len_bits(sae->tmp->ec); /* @@ -457,8 +501,10 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, * (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; + &qr, &qnr) < 0 || + crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 || + crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0) + goto fail; wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", password, password_len); @@ -474,7 +520,7 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, */ sae_pwd_seed_key(addr1, addr2, addrs); - addr[0] = password; + addr[0] = tmp_password; len[0] = password_len; num_elem = 1; if (identifier) { @@ -491,9 +537,8 @@ 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 || !x; counter++) { + for (counter = 1; counter <= k || !found; counter++) { u8 pwd_seed[SHA256_MAC_LEN]; - struct crypto_bignum *x_cand; if (counter > 200) { /* This should not happen in practice */ @@ -501,36 +546,45 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, break; } - wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); + wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter); + const_time_select_bin(found, dummy_password, password, + password_len, tmp_password); if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, addr, len, pwd_seed) < 0) break; res = sae_test_pwd_seed_ecc(sae, pwd_seed, - prime, qr, qnr, &x_cand); + prime, qr_bin, qnr_bin, x_cand_bin); + const_time_select_bin(found, x_bin, x_cand_bin, prime_len, + x_bin); + pwd_seed_odd = const_time_select_u8( + found, pwd_seed_odd, + pwd_seed[SHA256_MAC_LEN - 1] & 0x01); + os_memset(pwd_seed, 0, sizeof(pwd_seed)); if (res < 0) 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)); + /* Need to minimize differences in handling res == 0 and 1 here + * to avoid differences in timing and instruction cache access, + * so use const_time_select_*() to make local copies of the + * values based on whether this loop iteration was the one that + * found the pwd-seed/x. */ + + /* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them + * (with res converted to 0/0xff) handles this in constant time. + */ + found |= res * 0xff; + wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x", + res, found); + } - /* - * 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); - } + if (!found) { + wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); + res = -1; + goto fail; } + x = crypto_bignum_init_set(x_bin, prime_len); if (!x) { - wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); res = -1; goto fail; } @@ -543,7 +597,6 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, 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 @@ -555,27 +608,48 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, fail: crypto_bignum_deinit(qr, 0); crypto_bignum_deinit(qnr, 0); + os_free(dummy_password); + bin_clear_free(tmp_password, password_len); + crypto_bignum_deinit(x, 1); + os_memset(x_bin, 0, sizeof(x_bin)); + os_memset(x_cand_bin, 0, sizeof(x_cand_bin)); return res; } +static int sae_modp_group_require_masking(int group) +{ + /* Groups for which pwd-value is likely to be >= p frequently */ + return group == 22 || group == 23 || group == 24; +} + + static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len, const char *identifier) { - u8 counter; + u8 counter, k, sel_counter = 0; u8 addrs[2 * ETH_ALEN]; const u8 *addr[3]; size_t len[3]; size_t num_elem; - int found = 0; - - if (sae->tmp->pwe_ffc == NULL) { - sae->tmp->pwe_ffc = crypto_bignum_init(); - if (sae->tmp->pwe_ffc == NULL) - return -1; - } + u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* + * mask */ + u8 mask; + struct crypto_bignum *pwe; + size_t prime_len = sae->tmp->prime_len * 8; + u8 *pwe_buf; + + crypto_bignum_deinit(sae->tmp->pwe_ffc, 1); + sae->tmp->pwe_ffc = NULL; + + /* Allocate a buffer to maintain selected and candidate PWE for constant + * time selection. */ + pwe_buf = os_zalloc(prime_len * 2); + pwe = crypto_bignum_init(); + if (!pwe_buf || !pwe) + goto fail; wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", password, password_len); @@ -599,7 +673,9 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, len[num_elem] = sizeof(counter); num_elem++; - for (counter = 1; !found; counter++) { + k = sae_modp_group_require_masking(sae->group) ? 40 : 1; + + for (counter = 1; counter <= k || !found; counter++) { u8 pwd_seed[SHA256_MAC_LEN]; int res; @@ -609,20 +685,37 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, break; } - wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); + wpa_printf(MSG_DEBUG, "SAE: counter = %02u", counter); if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, addr, len, pwd_seed) < 0) break; - res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc); + res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe); + /* res is -1 for fatal failure, 0 if a valid PWE was not found, + * or 1 if a valid PWE was found. */ if (res < 0) break; - if (res > 0) { - wpa_printf(MSG_DEBUG, "SAE: Use this PWE"); - found = 1; - } + /* Store the candidate PWE into the second half of pwe_buf and + * the selected PWE in the beginning of pwe_buf using constant + * time selection. */ + if (crypto_bignum_to_bin(pwe, pwe_buf + prime_len, prime_len, + prime_len) < 0) + break; + const_time_select_bin(found, pwe_buf, pwe_buf + prime_len, + prime_len, pwe_buf); + sel_counter = const_time_select_u8(found, sel_counter, counter); + mask = const_time_eq_u8(res, 1); + found = const_time_select_u8(found, found, mask); } - return found ? 0 : -1; + if (!found) + goto fail; + + wpa_printf(MSG_DEBUG, "SAE: Use PWE from counter = %02u", sel_counter); + sae->tmp->pwe_ffc = crypto_bignum_init_set(pwe_buf, prime_len); +fail: + crypto_bignum_deinit(pwe, 1); + bin_clear_free(pwe_buf, prime_len * 2); + return sae->tmp->pwe_ffc ? 0 : -1; } @@ -1394,23 +1487,31 @@ int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data)); - if (sae->tmp == NULL) { + if (!sae->tmp || !sae->peer_commit_scalar || + !sae->tmp->own_commit_scalar) { wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available"); return -1; } - if (sae->tmp->ec) + if (sae->tmp->ec) { + if (!sae->tmp->peer_commit_element_ecc || + !sae->tmp->own_commit_element_ecc) + return -1; sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar, sae->tmp->peer_commit_element_ecc, sae->tmp->own_commit_scalar, sae->tmp->own_commit_element_ecc, verifier); - else + } else { + if (!sae->tmp->peer_commit_element_ffc || + !sae->tmp->own_commit_element_ffc) + return -1; sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar, sae->tmp->peer_commit_element_ffc, sae->tmp->own_commit_scalar, sae->tmp->own_commit_element_ffc, verifier); + } if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) { wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch"); diff --git a/src/common/sae.h b/src/common/sae.h index 3fbcb58d155a0..3eb6e323a68ff 100644 --- a/src/common/sae.h +++ b/src/common/sae.h @@ -40,6 +40,8 @@ struct sae_temporary_data { struct crypto_bignum *order_buf; struct wpabuf *anti_clogging_token; char *pw_id; + int vlan_id; + u8 bssid[ETH_ALEN]; }; enum sae_state { diff --git a/src/common/version.h b/src/common/version.h index 2f47903d407c2..06fc5e4d25a34 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -9,6 +9,6 @@ #define GIT_VERSION_STR_POSTFIX "" #endif /* GIT_VERSION_STR_POSTFIX */ -#define VERSION_STR "2.7" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX +#define VERSION_STR "2.8" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX #endif /* VERSION_H */ diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 2d989048ac943..ed2d1c2a02363 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -340,14 +340,21 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy * PTK = PRF-X(PMK, "Pairwise key expansion", * Min(AA, SA) || Max(AA, SA) || - * Min(ANonce, SNonce) || Max(ANonce, SNonce)) + * Min(ANonce, SNonce) || Max(ANonce, SNonce) + * [ || Z.x ]) + * + * The optional Z.x component is used only with DPP and that part is not defined + * in IEEE 802.11. */ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, const u8 *addr1, const u8 *addr2, const u8 *nonce1, const u8 *nonce2, - struct wpa_ptk *ptk, int akmp, int cipher) + struct wpa_ptk *ptk, int akmp, int cipher, + const u8 *z, size_t z_len) { - u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN]; +#define MAX_Z_LEN 66 /* with NIST P-521 */ + u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN + MAX_Z_LEN]; + size_t data_len = 2 * ETH_ALEN + 2 * WPA_NONCE_LEN; u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN]; size_t ptk_len; @@ -356,6 +363,9 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, return -1; } + if (z_len > MAX_Z_LEN) + return -1; + if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) { os_memcpy(data, addr1, ETH_ALEN); os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN); @@ -374,6 +384,11 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, WPA_NONCE_LEN); } + if (z && z_len) { + os_memcpy(data + 2 * ETH_ALEN + 2 * WPA_NONCE_LEN, z, z_len); + data_len += z_len; + } + ptk->kck_len = wpa_kck_len(akmp, pmk_len); ptk->kek_len = wpa_kek_len(akmp, pmk_len); ptk->tk_len = wpa_cipher_key_len(cipher); @@ -388,7 +403,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, if (wpa_key_mgmt_sha384(akmp)) { #if defined(CONFIG_SUITEB192) || defined(CONFIG_FILS) wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)"); - if (sha384_prf(pmk, pmk_len, label, data, sizeof(data), + if (sha384_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; #else /* CONFIG_SUITEB192 || CONFIG_FILS */ @@ -397,7 +412,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, } else if (wpa_key_mgmt_sha256(akmp) || akmp == WPA_KEY_MGMT_OWE) { #if defined(CONFIG_IEEE80211W) || defined(CONFIG_SAE) || defined(CONFIG_FILS) wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)"); - if (sha256_prf(pmk, pmk_len, label, data, sizeof(data), + if (sha256_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; #else /* CONFIG_IEEE80211W or CONFIG_SAE or CONFIG_FILS */ @@ -406,17 +421,17 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, #ifdef CONFIG_DPP } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 32) { wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)"); - if (sha256_prf(pmk, pmk_len, label, data, sizeof(data), + if (sha256_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 48) { wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)"); - if (sha384_prf(pmk, pmk_len, label, data, sizeof(data), + if (sha384_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 64) { wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)"); - if (sha512_prf(pmk, pmk_len, label, data, sizeof(data), + if (sha512_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; } else if (akmp == WPA_KEY_MGMT_DPP) { @@ -426,7 +441,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, #endif /* CONFIG_DPP */ } else { wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA1)"); - if (sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp, + if (sha1_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; } @@ -435,6 +450,8 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, MAC2STR(addr1), MAC2STR(addr2)); wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN); + if (z && z_len) + wpa_hexdump_key(MSG_DEBUG, "WPA: Z.x", z, z_len); wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len); wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", tmp, ptk_len); @@ -451,6 +468,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, ptk->kck2_len = 0; os_memset(tmp, 0, sizeof(tmp)); + os_memset(data, 0, data_len); return 0; } @@ -880,6 +898,12 @@ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, parse->igtk_len = len; break; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + case FTIE_SUBELEM_OCI: + parse->oci = pos; + parse->oci_len = len; + break; +#endif /* CONFIG_OCV */ default: wpa_printf(MSG_DEBUG, "FT: Unknown subelem id %u", id); break; @@ -1203,6 +1227,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, left = rsn_ie_len - 6; data->group_cipher = WPA_CIPHER_GTK_NOT_USED; + data->has_group = 1; data->key_mgmt = WPA_KEY_MGMT_OSEN; data->proto = WPA_PROTO_OSEN; } else { @@ -1224,6 +1249,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, if (left >= RSN_SELECTOR_LEN) { data->group_cipher = rsn_selector_to_bitfield(pos); + data->has_group = 1; if (!wpa_cipher_valid_group(data->group_cipher)) { wpa_printf(MSG_DEBUG, "%s: invalid group cipher 0x%x (%08x)", @@ -1249,6 +1275,8 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, "count %u left %u", __func__, count, left); return -4; } + if (count) + data->has_pairwise = 1; for (i = 0; i < count; i++) { data->pairwise_cipher |= rsn_selector_to_bitfield(pos); pos += RSN_SELECTOR_LEN; @@ -1467,6 +1495,15 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, } +int wpa_default_rsn_cipher(int freq) +{ + if (freq > 56160) + return WPA_CIPHER_GCMP; /* DMG */ + + return WPA_CIPHER_CCMP; +} + + #ifdef CONFIG_IEEE80211R /** @@ -1754,7 +1791,7 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, os_memcpy(ptk->tk, tmp + offset, ptk->tk_len); offset += ptk->tk_len; os_memcpy(ptk->kck2, tmp + offset, ptk->kck2_len); - offset = ptk->kck2_len; + offset += ptk->kck2_len; os_memcpy(ptk->kek2, tmp + offset, ptk->kek2_len); wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len); diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 62617444058b4..e83d6887a1cd5 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -110,6 +110,7 @@ WPA_CIPHER_BIP_CMAC_256) #define RSN_KEY_DATA_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 10) #define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11) #define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12) +#define RSN_KEY_DATA_OCI RSN_SELECTOR(0x00, 0x0f, 0xac, 13) #define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4) #define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5) @@ -148,7 +149,8 @@ WPA_CIPHER_BIP_CMAC_256) #define WPA_CAPABILITY_SPP_A_MSDU_REQUIRED BIT(11) #define WPA_CAPABILITY_PBAC BIT(12) #define WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST BIT(13) -/* B14-B15: Reserved */ +#define WPA_CAPABILITY_OCVC BIT(14) +/* B15: Reserved */ /* IEEE 802.11r */ @@ -326,6 +328,7 @@ struct rsn_ftie_sha384 { #define FTIE_SUBELEM_GTK 2 #define FTIE_SUBELEM_R0KH_ID 3 #define FTIE_SUBELEM_IGTK 4 +#define FTIE_SUBELEM_OCI 5 struct rsn_rdie { u8 id; @@ -344,7 +347,8 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, const u8 *addr1, const u8 *addr2, const u8 *nonce1, const u8 *nonce2, - struct wpa_ptk *ptk, int akmp, int cipher); + struct wpa_ptk *ptk, int akmp, int cipher, + const u8 *z, size_t z_len); int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len, const u8 *snonce, const u8 *anonce, const u8 *dh_ss, size_t dh_ss_len, u8 *pmk, size_t *pmk_len); @@ -389,7 +393,9 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, const u8 *snonce, struct wpa_ie_data { int proto; int pairwise_cipher; + int has_pairwise; int group_cipher; + int has_group; int key_mgmt; int capabilities; size_t num_pmkid; @@ -402,6 +408,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data); int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ie_data *data); +int wpa_default_rsn_cipher(int freq); void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, u8 *pmkid, int akmp); @@ -451,6 +458,10 @@ struct wpa_ft_ies { size_t tie_len; const u8 *igtk; size_t igtk_len; +#ifdef CONFIG_OCV + const u8 *oci; + size_t oci_len; +#endif /* CONFIG_OCV */ const u8 *ric; size_t ric_len; int key_mgmt; diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c index 623c2a768e43d..c9890a0e49050 100644 --- a/src/common/wpa_ctrl.c +++ b/src/common/wpa_ctrl.c @@ -11,6 +11,8 @@ #ifdef CONFIG_CTRL_IFACE #ifdef CONFIG_CTRL_IFACE_UNIX +#include <sys/stat.h> +#include <fcntl.h> #include <sys/un.h> #include <unistd.h> #include <fcntl.h> @@ -133,6 +135,19 @@ try_again: return NULL; } tries++; +#ifdef ANDROID + /* Set client socket file permissions so that bind() creates the client + * socket with these permissions and there is no need to try to change + * them with chmod() after bind() which would have potential issues with + * race conditions. These permissions are needed to make sure the server + * side (wpa_supplicant or hostapd) can reply to the control interface + * messages. + * + * The lchown() calls below after bind() are also part of the needed + * operations to allow the response to go through. Those are using the + * no-deference-symlinks version to avoid races. */ + fchmod(ctrl->s, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); +#endif /* ANDROID */ if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, sizeof(ctrl->local)) < 0) { if (errno == EADDRINUSE && tries < 2) { @@ -151,10 +166,9 @@ 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); + lchown(ctrl->local.sun_path, -1, AID_WIFI); + lchown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI); if (os_strncmp(ctrl_path, "@android:", 9) == 0) { if (socket_local_client_connect( @@ -540,7 +554,8 @@ retry_send: res = recv(ctrl->s, reply, *reply_len, 0); if (res < 0) return res; - if (res > 0 && reply[0] == '<') { + if ((res > 0 && reply[0] == '<') || + (res > 6 && strncmp(reply, "IFNAME=", 7) == 0)) { /* This is an unsolicited message from * wpa_supplicant, not the reply to the * request. Use msg_cb to report this to the diff --git a/src/crypto/Makefile b/src/crypto/Makefile index ee93e41fbbb17..ab108daac835e 100644 --- a/src/crypto/Makefile +++ b/src/crypto/Makefile @@ -62,7 +62,9 @@ LIB_OBJS += crypto_internal-modexp.o LIB_OBJS += crypto_internal-rsa.o LIB_OBJS += tls_internal.o LIB_OBJS += fips_prf_internal.o +ifndef TEST_FUZZ LIB_OBJS += random.o +endif libcrypto.a: $(LIB_OBJS) diff --git a/src/crypto/aes-internal-enc.c b/src/crypto/aes-internal-enc.c index 9fdb4f35cb9b7..baeffcaf630c3 100644 --- a/src/crypto/aes-internal-enc.c +++ b/src/crypto/aes-internal-enc.c @@ -99,6 +99,10 @@ void * aes_encrypt_init(const u8 *key, size_t len) { u32 *rk; int res; + + if (TEST_FAIL()) + return NULL; + rk = os_malloc(AES_PRIV_SIZE); if (rk == NULL) return NULL; diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 507b7cab86fc7..12109ce83a9ad 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -420,6 +420,7 @@ int __must_check crypto_public_key_decrypt_pkcs1( int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, u8 *pubkey); int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len); @@ -703,14 +704,6 @@ struct crypto_ec * crypto_ec_init(int group); void crypto_ec_deinit(struct crypto_ec *e); /** - * crypto_ec_cofactor - Set the cofactor into the big number - * @e: EC context from crypto_ec_init() - * @cofactor: Cofactor of curve. - * Returns: 0 on success, -1 on failure - */ -int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor); - -/** * crypto_ec_prime_len - Get length of the prime in octets * @e: EC context from crypto_ec_init() * Returns: Length of the prime defining the group diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c index 7a797b5c359d1..4ef11462b36e8 100644 --- a/src/crypto/crypto_gnutls.c +++ b/src/crypto/crypto_gnutls.c @@ -310,12 +310,51 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { - return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, - prime, prime_len, secret, len); + gcry_mpi_t pub = NULL; + int res = -1; + + if (pubkey_len > prime_len || + (pubkey_len == prime_len && + os_memcmp(pubkey, prime, prime_len) >= 0)) + return -1; + + if (gcry_mpi_scan(&pub, GCRYMPI_FMT_USG, pubkey, pubkey_len, NULL) != + GPG_ERR_NO_ERROR || + gcry_mpi_cmp_ui(pub, 1) <= 0) + goto fail; + + if (order) { + gcry_mpi_t p = NULL, q = NULL, tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + tmp = gcry_mpi_new(prime_len * 8); + failed = !tmp || + gcry_mpi_scan(&p, GCRYMPI_FMT_USG, prime, prime_len, + NULL) != GPG_ERR_NO_ERROR || + gcry_mpi_scan(&q, GCRYMPI_FMT_USG, order, order_len, + NULL) != GPG_ERR_NO_ERROR; + if (!failed) { + gcry_mpi_powm(tmp, pub, q, p); + failed = gcry_mpi_cmp_ui(tmp, 1) != 0; + } + gcry_mpi_release(p); + gcry_mpi_release(q); + gcry_mpi_release(tmp); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + gcry_mpi_release(pub); + return res; } diff --git a/src/crypto/crypto_internal-modexp.c b/src/crypto/crypto_internal-modexp.c index 92581ac676d31..6819f1a6ab6a7 100644 --- a/src/crypto/crypto_internal-modexp.c +++ b/src/crypto/crypto_internal-modexp.c @@ -40,12 +40,49 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { - return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, - prime, prime_len, secret, len); + struct bignum *pub; + int res = -1; + + if (pubkey_len > prime_len || + (pubkey_len == prime_len && + os_memcmp(pubkey, prime, prime_len) >= 0)) + return -1; + + pub = bignum_init(); + if (!pub || bignum_set_unsigned_bin(pub, pubkey, pubkey_len) < 0 || + bignum_cmp_d(pub, 1) <= 0) + goto fail; + + if (order) { + struct bignum *p, *q, *tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + p = bignum_init(); + q = bignum_init(); + tmp = bignum_init(); + failed = !p || !q || !tmp || + bignum_set_unsigned_bin(p, prime, prime_len) < 0 || + bignum_set_unsigned_bin(q, order, order_len) < 0 || + bignum_exptmod(pub, q, p, tmp) < 0 || + bignum_cmp_d(tmp, 1) != 0; + bignum_deinit(p); + bignum_deinit(q); + bignum_deinit(tmp); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + bignum_deinit(pub); + return res; } diff --git a/src/crypto/crypto_internal.c b/src/crypto/crypto_internal.c index d391f48ab5b18..aad40af16e06e 100644 --- a/src/crypto/crypto_internal.c +++ b/src/crypto/crypto_internal.c @@ -310,6 +310,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) os_free(ctx); + if (TEST_FAIL()) + return -1; + return 0; } diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c index 259f99500bcd3..ed30efa021d76 100644 --- a/src/crypto/crypto_libtomcrypt.c +++ b/src/crypto/crypto_libtomcrypt.c @@ -278,6 +278,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) os_free(ctx); + if (TEST_FAIL()) + return -1; + return ret; } @@ -721,10 +724,12 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { + /* TODO: check pubkey */ return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, prime, prime_len, secret, len); } diff --git a/src/crypto/crypto_linux.c b/src/crypto/crypto_linux.c index 8099193bf0682..17244561b372f 100644 --- a/src/crypto/crypto_linux.c +++ b/src/crypto/crypto_linux.c @@ -386,6 +386,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) } crypto_hash_deinit(ctx); + + if (TEST_FAIL()) + return -1; return 0; } diff --git a/src/crypto/crypto_nettle.c b/src/crypto/crypto_nettle.c index 4e31bc8011325..f85d36532ea19 100644 --- a/src/crypto/crypto_nettle.c +++ b/src/crypto/crypto_nettle.c @@ -331,12 +331,44 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { - return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, - prime, prime_len, secret, len); + mpz_t pub; + int res = -1; + + if (pubkey_len > prime_len || + (pubkey_len == prime_len && + os_memcmp(pubkey, prime, prime_len) >= 0)) + return -1; + + mpz_init(pub); + mpz_import(pub, pubkey_len, 1, 1, 1, 0, pubkey); + if (mpz_cmp_d(pub, 1) <= 0) + goto fail; + + if (order) { + mpz_t p, q, tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + mpz_inits(p, q, tmp, NULL); + mpz_import(p, prime_len, 1, 1, 1, 0, prime); + mpz_import(q, order_len, 1, 1, 1, 0, order); + mpz_powm(tmp, pub, q, p); + failed = mpz_cmp_d(tmp, 1) != 0; + mpz_clears(p, q, tmp, NULL); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + mpz_clear(pub); + return res; } diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index f89053a89d67e..1b0c1ec96b36c 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -24,6 +24,7 @@ #endif /* CONFIG_ECC */ #include "common.h" +#include "utils/const_time.h" #include "wpabuf.h" #include "dh_group5.h" #include "sha1.h" @@ -111,6 +112,31 @@ static BIGNUM * get_group5_prime(void) #endif } + +static BIGNUM * get_group5_order(void) +{ + static const unsigned char RFC3526_ORDER_1536[] = { + 0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE4,0x87,0xED,0x51, + 0x10,0xB4,0x61,0x1A,0x62,0x63,0x31,0x45,0xC0,0x6E,0x0E,0x68, + 0x94,0x81,0x27,0x04,0x45,0x33,0xE6,0x3A,0x01,0x05,0xDF,0x53, + 0x1D,0x89,0xCD,0x91,0x28,0xA5,0x04,0x3C,0xC7,0x1A,0x02,0x6E, + 0xF7,0xCA,0x8C,0xD9,0xE6,0x9D,0x21,0x8D,0x98,0x15,0x85,0x36, + 0xF9,0x2F,0x8A,0x1B,0xA7,0xF0,0x9A,0xB6,0xB6,0xA8,0xE1,0x22, + 0xF2,0x42,0xDA,0xBB,0x31,0x2F,0x3F,0x63,0x7A,0x26,0x21,0x74, + 0xD3,0x1B,0xF6,0xB5,0x85,0xFF,0xAE,0x5B,0x7A,0x03,0x5B,0xF6, + 0xF7,0x1C,0x35,0xFD,0xAD,0x44,0xCF,0xD2,0xD7,0x4F,0x92,0x08, + 0xBE,0x25,0x8F,0xF3,0x24,0x94,0x33,0x28,0xF6,0x72,0x2D,0x9E, + 0xE1,0x00,0x3E,0x5C,0x50,0xB1,0xDF,0x82,0xCC,0x6D,0x24,0x1B, + 0x0E,0x2A,0xE9,0xCD,0x34,0x8B,0x1F,0xD4,0x7E,0x92,0x67,0xAF, + 0xC1,0xB2,0xAE,0x91,0xEE,0x51,0xD6,0xCB,0x0E,0x31,0x79,0xAB, + 0x10,0x42,0xA9,0x5D,0xCF,0x6A,0x94,0x83,0xB8,0x4B,0x4B,0x36, + 0xB3,0x86,0x1A,0xA7,0x25,0x5E,0x4C,0x02,0x78,0xBA,0x36,0x04, + 0x65,0x11,0xB9,0x93,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + }; + return BN_bin2bn(RFC3526_ORDER_1536, sizeof(RFC3526_ORDER_1536), NULL); +} + + #ifdef OPENSSL_NO_SHA256 #define NO_SHA256_WRAPPER #endif @@ -518,12 +544,45 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { - return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, - prime, prime_len, secret, len); + BIGNUM *pub, *p; + int res = -1; + + pub = BN_bin2bn(pubkey, pubkey_len, NULL); + p = BN_bin2bn(prime, prime_len, NULL); + if (!pub || !p || BN_is_zero(pub) || BN_is_one(pub) || + BN_cmp(pub, p) >= 0) + goto fail; + + if (order) { + BN_CTX *ctx; + BIGNUM *q, *tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + q = BN_bin2bn(order, order_len, NULL); + ctx = BN_CTX_new(); + tmp = BN_new(); + failed = !q || !ctx || !tmp || + !BN_mod_exp(tmp, pub, q, p, ctx) || + !BN_is_one(tmp); + BN_clear(q); + BN_clear(tmp); + BN_CTX_free(ctx); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + BN_clear(pub); + BN_clear(p); + return res; } @@ -549,7 +608,8 @@ int crypto_mod_exp(const u8 *base, size_t base_len, bn_result == NULL) goto error; - if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1) + if (BN_mod_exp_mont_consttime(bn_result, bn_base, bn_exp, bn_modulus, + ctx, NULL) != 1) goto error; *result_len = BN_bn2bin(bn_result, result); @@ -709,6 +769,10 @@ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) if (dh->p == NULL) goto err; + dh->q = get_group5_order(); + if (!dh->q) + goto err; + if (DH_generate_key(dh) != 1) goto err; @@ -737,7 +801,7 @@ err: DH *dh; struct wpabuf *pubkey = NULL, *privkey = NULL; size_t publen, privlen; - BIGNUM *p = NULL, *g; + BIGNUM *p, *g, *q; const BIGNUM *priv_key = NULL, *pub_key = NULL; *priv = NULL; @@ -750,10 +814,12 @@ err: g = BN_new(); p = get_group5_prime(); - if (!g || BN_set_word(g, 2) != 1 || !p || - DH_set0_pqg(dh, p, NULL, g) != 1) + q = get_group5_order(); + if (!g || BN_set_word(g, 2) != 1 || !p || !q || + DH_set0_pqg(dh, p, q, g) != 1) goto err; p = NULL; + q = NULL; g = NULL; if (DH_generate_key(dh) != 1) @@ -778,6 +844,7 @@ err: err: BN_free(p); + BN_free(q); BN_free(g); wpabuf_clear_free(pubkey); wpabuf_clear_free(privkey); @@ -987,6 +1054,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) HMAC_CTX_free(ctx->ctx); bin_clear_free(ctx, sizeof(*ctx)); + if (TEST_FAIL()) + return -1; + if (res == 1) { *len = mdlen; return 0; @@ -1250,6 +1320,8 @@ int crypto_bignum_to_bin(const struct crypto_bignum *a, int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m) { + if (TEST_FAIL()) + return -1; return BN_rand_range((BIGNUM *) r, (const BIGNUM *) m) == 1 ? 0 : -1; } @@ -1295,8 +1367,9 @@ int crypto_bignum_exptmod(const struct crypto_bignum *a, bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; - res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b, - (const BIGNUM *) c, bnctx); + res = BN_mod_exp_mont_consttime((BIGNUM *) d, (const BIGNUM *) a, + (const BIGNUM *) b, (const BIGNUM *) c, + bnctx, NULL); BN_CTX_free(bnctx); return res ? 0 : -1; @@ -1315,6 +1388,11 @@ int crypto_bignum_inverse(const struct crypto_bignum *a, bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; +#ifdef OPENSSL_IS_BORINGSSL + /* TODO: use BN_mod_inverse_blinded() ? */ +#else /* OPENSSL_IS_BORINGSSL */ + BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME); +#endif /* OPENSSL_IS_BORINGSSL */ res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b, bnctx); BN_CTX_free(bnctx); @@ -1348,6 +1426,9 @@ int crypto_bignum_div(const struct crypto_bignum *a, bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; +#ifndef OPENSSL_IS_BORINGSSL + BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME); +#endif /* OPENSSL_IS_BORINGSSL */ res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a, (const BIGNUM *) b, bnctx); BN_CTX_free(bnctx); @@ -1425,6 +1506,7 @@ int crypto_bignum_legendre(const struct crypto_bignum *a, BN_CTX *bnctx; BIGNUM *exp = NULL, *tmp = NULL; int res = -2; + unsigned int mask; if (TEST_FAIL()) return -2; @@ -1439,16 +1521,17 @@ int crypto_bignum_legendre(const struct crypto_bignum *a, /* 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)) + !BN_mod_exp_mont_consttime(tmp, (const BIGNUM *) a, exp, + (const BIGNUM *) p, bnctx, NULL)) goto fail; - if (BN_is_word(tmp, 1)) - res = 1; - else if (BN_is_zero(tmp)) - res = 0; - else - res = -1; + /* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need to use + * constant time selection to avoid branches here. */ + res = -1; + mask = const_time_eq(BN_is_word(tmp, 1), 1); + res = const_time_select_int(mask, 1, res); + mask = const_time_eq(BN_is_zero(tmp), 1); + res = const_time_select_int(mask, 0, res); fail: BN_clear_free(tmp); @@ -1553,13 +1636,6 @@ void crypto_ec_deinit(struct crypto_ec *e) } -int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor) -{ - return EC_GROUP_get_cofactor(e->group, (BIGNUM *) cofactor, - e->bnctx) == 0 ? -1 : 0; -} - - struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) { if (TEST_FAIL()) diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c index b5a1e3fa31bc1..976a008651b75 100644 --- a/src/crypto/crypto_wolfssl.c +++ b/src/crypto/crypto_wolfssl.c @@ -826,6 +826,7 @@ done: int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) @@ -952,6 +953,8 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) ret = 0; done: bin_clear_free(ctx, sizeof(*ctx)); + if (TEST_FAIL()) + return -1; return ret; } @@ -1082,6 +1085,8 @@ int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m) int ret = 0; WC_RNG rng; + if (TEST_FAIL()) + return -1; if (wc_InitRng(&rng) != 0) return -1; if (mp_rand_prime((mp_int *) r, @@ -1347,16 +1352,6 @@ void crypto_ec_deinit(struct crypto_ec* e) } -int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor) -{ - if (!e || !cofactor) - return -1; - - mp_set((mp_int *) cofactor, e->key.dp->cofactor); - return 0; -} - - struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) { if (TEST_FAIL()) diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c index a9b770ec1f160..5e421b24f5a56 100644 --- a/src/crypto/dh_groups.c +++ b/src/crypto/dh_groups.c @@ -1249,6 +1249,7 @@ struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public, if (shared == NULL) return NULL; if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len, + dh->order, dh->order_len, wpabuf_head(own_private), wpabuf_len(own_private), wpabuf_head(peer_public), diff --git a/src/crypto/md4-internal.c b/src/crypto/md4-internal.c index d9c737a2970b6..cf408e84fae6a 100644 --- a/src/crypto/md4-internal.c +++ b/src/crypto/md4-internal.c @@ -85,7 +85,7 @@ MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]); (cp)[1] = (value) >> 8; \ (cp)[0] = (value); } while (0) -static u8 PADDING[MD4_BLOCK_LENGTH] = { +static const u8 PADDING[MD4_BLOCK_LENGTH] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 diff --git a/src/crypto/random.c b/src/crypto/random.c index c278d9cb9de94..1cabf3f4b9a42 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -25,6 +25,9 @@ #include "utils/includes.h" #ifdef __linux__ #include <fcntl.h> +#ifdef CONFIG_GETRANDOM +#include <sys/random.h> +#endif /* CONFIG_GETRANDOM */ #endif /* __linux__ */ #include "utils/common.h" @@ -228,30 +231,52 @@ int random_pool_ready(void) return 1; /* Already initialized - good to continue */ /* - * Try to fetch some more data from the kernel high quality - * /dev/random. There may not be enough data available at this point, + * Try to fetch some more data from the kernel high quality RNG. + * There may not be enough data available at this point, * so use non-blocking read to avoid blocking the application * completely. */ - fd = open("/dev/random", O_RDONLY | O_NONBLOCK); - if (fd < 0) { - wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", - strerror(errno)); - return -1; - } - res = read(fd, dummy_key + dummy_key_avail, - sizeof(dummy_key) - dummy_key_avail); +#ifdef CONFIG_GETRANDOM + res = getrandom(dummy_key + dummy_key_avail, + sizeof(dummy_key) - dummy_key_avail, GRND_NONBLOCK); if (res < 0) { - wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: " - "%s", strerror(errno)); - res = 0; + if (errno == ENOSYS) { + wpa_printf(MSG_DEBUG, + "random: getrandom() not supported, falling back to /dev/random"); + } else { + wpa_printf(MSG_INFO, + "random: no data from getrandom(): %s", + strerror(errno)); + res = 0; + } + } +#else /* CONFIG_GETRANDOM */ + res = -1; +#endif /* CONFIG_GETRANDOM */ + if (res < 0) { + fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (fd < 0) { + wpa_printf(MSG_ERROR, + "random: Cannot open /dev/random: %s", + strerror(errno)); + return -1; + } + + res = read(fd, dummy_key + dummy_key_avail, + sizeof(dummy_key) - dummy_key_avail); + if (res < 0) { + wpa_printf(MSG_ERROR, + "random: Cannot read from /dev/random: %s", + strerror(errno)); + res = 0; + } + close(fd); } - wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from " - "/dev/random", (unsigned) res, + + wpa_printf(MSG_DEBUG, "random: Got %u/%u random bytes", (unsigned) res, (unsigned) (sizeof(dummy_key) - dummy_key_avail)); dummy_key_avail += res; - close(fd); if (dummy_key_avail == sizeof(dummy_key)) { if (own_pool_ready < MIN_READY_MARK) @@ -261,7 +286,7 @@ int random_pool_ready(void) } wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong " - "random data available from /dev/random", + "random data available", (unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key)); if (own_pool_ready >= MIN_READY_MARK || @@ -413,6 +438,19 @@ void random_init(const char *entropy_file) if (random_fd >= 0) return; +#ifdef CONFIG_GETRANDOM + { + u8 dummy; + + if (getrandom(&dummy, 0, GRND_NONBLOCK) == 0 || + errno != ENOSYS) { + wpa_printf(MSG_DEBUG, + "random: getrandom() support available"); + return; + } + } +#endif /* CONFIG_GETRANDOM */ + random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK); if (random_fd < 0) { wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", diff --git a/src/crypto/sha1-tlsprf.c b/src/crypto/sha1-tlsprf.c index f9bc0ebf6e3d0..a11649a933eb3 100644 --- a/src/crypto/sha1-tlsprf.c +++ b/src/crypto/sha1-tlsprf.c @@ -40,9 +40,6 @@ int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label, const unsigned char *SHA1_addr[3]; size_t SHA1_len[3]; - if (secret_len & 1) - return -1; - MD5_addr[0] = A_MD5; MD5_len[0] = MD5_MAC_LEN; MD5_addr[1] = (unsigned char *) label; diff --git a/src/crypto/sha512-internal.c b/src/crypto/sha512-internal.c index 76c4fe750b65f..c0263941c123c 100644 --- a/src/crypto/sha512-internal.c +++ b/src/crypto/sha512-internal.c @@ -109,9 +109,14 @@ static const u64 K[80] = { /* compress 1024-bits */ static int sha512_compress(struct sha512_state *md, unsigned char *buf) { - u64 S[8], W[80], t0, t1; + u64 S[8], t0, t1; + u64 *W; int i; + W = os_malloc(80 * sizeof(u64)); + if (!W) + return -1; + /* copy state into S */ for (i = 0; i < 8; i++) { S[i] = md->state[i]; @@ -146,6 +151,7 @@ static int sha512_compress(struct sha512_state *md, unsigned char *buf) md->state[i] = md->state[i] + S[i]; } + os_free(W); return 0; } diff --git a/src/crypto/sha512.c b/src/crypto/sha512.c new file mode 100644 index 0000000000000..66311c3739208 --- /dev/null +++ b/src/crypto/sha512.c @@ -0,0 +1,104 @@ +/* + * SHA-512 hash implementation and interface functions + * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha512.h" +#include "crypto.h" + + +/** + * hmac_sha512_vector - HMAC-SHA512 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (64 bytes) + * Returns: 0 on success, -1 on failure + */ +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + unsigned char k_pad[128]; /* padding - key XORd with ipad/opad */ + unsigned char tk[64]; + const u8 *_addr[6]; + size_t _len[6], i; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return -1; + } + + /* if key is longer than 128 bytes reset it to key = SHA512(key) */ + if (key_len > 128) { + if (sha512_vector(1, &key, &key_len, tk) < 0) + return -1; + key = tk; + key_len = 64; + } + + /* the HMAC_SHA512 transform looks like: + * + * SHA512(K XOR opad, SHA512(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 128 times + * opad is the byte 0x5c repeated 128 times + * and text is the data being protected */ + + /* start out by storing key in ipad */ + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with ipad values */ + for (i = 0; i < 128; i++) + k_pad[i] ^= 0x36; + + /* perform inner SHA512 */ + _addr[0] = k_pad; + _len[0] = 128; + for (i = 0; i < num_elem; i++) { + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; + } + if (sha512_vector(1 + num_elem, _addr, _len, mac) < 0) + return -1; + + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 128; i++) + k_pad[i] ^= 0x5c; + + /* perform outer SHA512 */ + _addr[0] = k_pad; + _len[0] = 128; + _addr[1] = mac; + _len[1] = SHA512_MAC_LEN; + return sha512_vector(2, _addr, _len, mac); +} + + +/** + * hmac_sha512 - HMAC-SHA512 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (64 bytes) + * Returns: 0 on success, -1 on failure + */ +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac); +} diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 481b34681d7ba..8bdb91ff2469c 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -42,6 +42,7 @@ enum tls_fail_reason { TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9, TLS_FAIL_DOMAIN_MISMATCH = 10, TLS_FAIL_INSUFFICIENT_KEY_LEN = 11, + TLS_FAIL_DN_MISMATCH = 12, }; @@ -82,6 +83,7 @@ struct tls_config { int cert_in_cb; const char *openssl_ciphers; unsigned int tls_session_lifetime; + unsigned int crl_reload_interval; unsigned int tls_flags; void (*event_cb)(void *ctx, enum tls_event ev, @@ -103,6 +105,9 @@ struct tls_config { #define TLS_CONN_SUITEB BIT(11) #define TLS_CONN_SUITEB_NO_ECDH BIT(12) #define TLS_CONN_DISABLE_TLSv1_3 BIT(13) +#define TLS_CONN_ENABLE_TLSv1_0 BIT(14) +#define TLS_CONN_ENABLE_TLSv1_1 BIT(15) +#define TLS_CONN_ENABLE_TLSv1_2 BIT(16) /** * struct tls_connection_params - Parameters for TLS connection @@ -115,12 +120,19 @@ struct tls_config { * %NULL to allow all subjects * @altsubject_match: String to match in the alternative subject of the peer * certificate or %NULL to allow all alternative subjects - * @suffix_match: String to suffix match in the dNSName or CN of the peer - * certificate or %NULL to allow all domain names. This may allow subdomains an - * wildcard certificates. Each domain name label must have a full match. + * @suffix_match: Semicolon deliminated string of values to suffix match against + * the dNSName or CN of the peer certificate or %NULL to allow all domain names. + * This may allow subdomains and wildcard certificates. Each domain name label + * must have a full case-insensitive match. * @domain_match: String to match in the dNSName or CN of the peer * certificate or %NULL to allow all domain names. This requires a full, * case-insensitive match. + * + * More than one match string can be provided by using semicolons to + * separate the strings (e.g., example.org;example.com). When multiple + * strings are specified, a match with any one of the values is + * considered a sufficient match for the certificate, i.e., the + * conditions are ORed together. * @client_cert: File or reference name for client X.509 certificate in PEM or * DER format * @client_cert_blob: client_cert as inlined data or %NULL if not used @@ -144,12 +156,15 @@ struct tls_config { * @cert_id: the certificate's id when using engine * @ca_cert_id: the CA certificate's id when using engine * @openssl_ciphers: OpenSSL cipher configuration + * @openssl_ecdh_curves: OpenSSL ECDH curve configuration. %NULL for auto if + * supported, empty string to disable, or a colon-separated curve list. * @flags: Parameter options (TLS_CONN_*) * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response * or %NULL if OCSP is not enabled * @ocsp_stapling_response_multi: DER encoded file with cached OCSP stapling * response list (OCSPResponseList for ocsp_multi in RFC 6961) or %NULL if * ocsp_multi is not enabled + * @check_cert_subject: Client certificate subject name matching string * * TLS connection parameters to be configured with tls_connection_set_params() * and tls_global_set_params(). @@ -187,10 +202,12 @@ struct tls_connection_params { const char *cert_id; const char *ca_cert_id; const char *openssl_ciphers; + const char *openssl_ecdh_curves; unsigned int flags; const char *ocsp_stapling_response; const char *ocsp_stapling_response_multi; + const char *check_cert_subject; }; @@ -321,9 +338,11 @@ int __must_check tls_global_set_params( * @tls_ctx: TLS context data from tls_init() * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate, * 2 = verify CRL for all certificates + * @strict: 0 = allow CRL time errors, 1 = do not allow CRL time errors * Returns: 0 on success, -1 on failure */ -int __must_check tls_global_set_verify(void *tls_ctx, int check_crl); +int __must_check tls_global_set_verify(void *tls_ctx, int check_crl, + int strict); /** * tls_connection_set_verify - Set certificate verification options @@ -358,15 +377,21 @@ int __must_check tls_connection_get_random(void *tls_ctx, * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @label: Label (e.g., description of the key) for PRF + * @context: Optional extra upper-layer context (max len 2^16) + * @context_len: The length of the context value * @out: Buffer for output data from TLS-PRF * @out_len: Length of the output buffer * Returns: 0 on success, -1 on failure * - * Exports keying material using the mechanism described in RFC 5705. + * Exports keying material using the mechanism described in RFC 5705. If + * context is %NULL, context is not provided; otherwise, context is provided + * (including the case of empty context with context_len == 0). */ int __must_check tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, const char *label, + const u8 *context, + size_t context_len, u8 *out, size_t out_len); /** diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c index 36dafd2603f0c..daa01d9ed4f64 100644 --- a/src/crypto/tls_gnutls.c +++ b/src/crypto/tls_gnutls.c @@ -461,6 +461,12 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } } + if (params->openssl_ecdh_curves) { + wpa_printf(MSG_INFO, + "GnuTLS: openssl_ecdh_curves not supported"); + return -1; + } + /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); * to force peer validation(?) */ @@ -733,6 +739,9 @@ int tls_global_set_params(void *tls_ctx, struct tls_global *global = tls_ctx; int ret; + if (params->check_cert_subject) + return -1; /* not yet supported */ + /* Currently, global parameters are only set when running in server * mode. */ global->server = 1; @@ -842,7 +851,7 @@ fail: } -int tls_global_set_verify(void *ssl_ctx, int check_crl) +int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict) { /* TODO */ return 0; @@ -889,14 +898,23 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { if (conn == NULL || conn->session == NULL) return -1; +#if GNUTLS_VERSION_NUMBER >= 0x030404 + return gnutls_prf_rfc5705(conn->session, os_strlen(label), label, + context_len, (const char *) context, + out_len, (char *) out); +#else /* 3.4.4 */ + if (context) + return -1; return gnutls_prf(conn->session, os_strlen(label), label, 0 /* client_random first */, 0, NULL, out_len, (char *) out); +#endif /* 3.4.4 */ } @@ -1068,6 +1086,52 @@ ocsp_error: } +static int tls_match_suffix_helper(gnutls_x509_crt_t cert, const char *match, + int full) +{ + int res = -1; + +#if GNUTLS_VERSION_NUMBER >= 0x030300 + if (full) + res = gnutls_x509_crt_check_hostname2( + cert, match, + GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS); +#endif /* >= 3.3.0 */ + if (res == -1) + res = gnutls_x509_crt_check_hostname(cert, match); + + wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s --> res=%d", + full ? "": "suffix ", match, res); + return res; +} + + +static int tls_match_suffix(gnutls_x509_crt_t cert, const char *match, + int full) +{ + char *values, *token, *context = NULL; + int ret = 0; + + if (!os_strchr(match, ';')) + return tls_match_suffix_helper(cert, match, full); + + values = os_strdup(match); + if (!values) + return 0; + + /* Process each match alternative separately until a match is found */ + while ((token = str_token(values, ";", &context))) { + if (tls_match_suffix_helper(cert, token, full)) { + ret = 1; + break; + } + } + + os_free(values); + return ret; +} + + static int tls_connection_verify_peer(gnutls_session_t session) { struct tls_connection *conn; @@ -1263,8 +1327,7 @@ static int tls_connection_verify_peer(gnutls_session_t session) if (i == 0) { if (conn->suffix_match && - !gnutls_x509_crt_check_hostname( - cert, conn->suffix_match)) { + !tls_match_suffix(cert, conn->suffix_match, 0)) { wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found", conn->suffix_match); @@ -1280,9 +1343,7 @@ static int tls_connection_verify_peer(gnutls_session_t session) #if GNUTLS_VERSION_NUMBER >= 0x030300 if (conn->domain_match && - !gnutls_x509_crt_check_hostname2( - cert, conn->domain_match, - GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) { + !tls_match_suffix(cert, conn->domain_match, 1)) { wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found", conn->domain_match); diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c index d289c9442ceb3..8095b43bd21bc 100644 --- a/src/crypto/tls_internal.c +++ b/src/crypto/tls_internal.c @@ -1,6 +1,6 @@ /* * TLS interface functions and an internal TLS implementation - * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -248,6 +248,12 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } + if (params->openssl_ecdh_curves) { + wpa_printf(MSG_INFO, "TLS: openssl_ecdh_curves not supported"); + tlsv1_cred_free(cred); + return -1; + } + if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob, params->ca_cert_blob_len, params->ca_path)) { @@ -303,6 +309,9 @@ int tls_global_set_params(void *tls_ctx, struct tls_global *global = tls_ctx; struct tlsv1_credentials *cred; + if (params->check_cert_subject) + return -1; /* not yet supported */ + /* Currently, global parameters are only set when running in server * mode. */ global->server = 1; @@ -353,7 +362,7 @@ int tls_global_set_params(void *tls_ctx, } -int tls_global_set_verify(void *tls_ctx, int check_crl) +int tls_global_set_verify(void *tls_ctx, int check_crl, int strict) { struct tls_global *global = tls_ctx; global->check_crl = check_crl; @@ -403,7 +412,8 @@ static int tls_get_keyblock_size(struct tls_connection *conn) static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, + const char *label, const u8 *context, + size_t context_len, int server_random_first, int skip_keyblock, u8 *out, size_t out_len) { int ret = -1, skip = 0; @@ -422,15 +432,15 @@ static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { - ret = tlsv1_client_prf(conn->client, label, - server_random_first, + ret = tlsv1_client_prf(conn->client, label, context, + context_len, server_random_first, _out, skip + out_len); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) { - ret = tlsv1_server_prf(conn->server, label, - server_random_first, + ret = tlsv1_server_prf(conn->server, label, context, + context_len, server_random_first, _out, skip + out_len); } #endif /* CONFIG_TLS_INTERNAL_SERVER */ @@ -443,17 +453,19 @@ static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { - return tls_connection_prf(tls_ctx, conn, label, 0, 0, out, out_len); + return tls_connection_prf(tls_ctx, conn, label, context, context_len, + 0, 0, out, out_len); } int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, u8 *out, size_t out_len) { - return tls_connection_prf(tls_ctx, conn, "key expansion", 1, 1, out, - out_len); + return tls_connection_prf(tls_ctx, conn, "key expansion", NULL, 0, + 1, 1, out, out_len); } @@ -720,12 +732,20 @@ int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) { +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_failed(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) { +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_read_alerts(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } @@ -733,6 +753,10 @@ 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) { +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_write_alerts(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c index 5d0c6bda15479..6d6fb0cafd31c 100644 --- a/src/crypto/tls_none.c +++ b/src/crypto/tls_none.c @@ -72,7 +72,7 @@ int tls_global_set_params(void *tls_ctx, } -int tls_global_set_verify(void *tls_ctx, int check_crl) +int tls_global_set_verify(void *tls_ctx, int check_crl, int strict) { return -1; } @@ -94,7 +94,8 @@ int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { return -1; } diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index 0d5ebda699f02..b0c23ae6c9b1f 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -104,7 +104,9 @@ static size_t SSL_SESSION_get_master_key(const SSL_SESSION *session, #endif -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) #ifdef CONFIG_SUITEB static int RSA_bits(const RSA *r) { @@ -212,10 +214,17 @@ static struct tls_context *tls_global = NULL; struct tls_data { SSL_CTX *ssl; unsigned int tls_session_lifetime; + int check_crl; + int check_crl_strict; + char *ca_cert; + unsigned int crl_reload_interval; + struct os_reltime crl_last_reload; + char *check_cert_subject; }; struct tls_connection { struct tls_context *context; + struct tls_data *data; SSL_CTX *ssl_ctx; SSL *ssl; BIO *ssl_in, *ssl_out; @@ -224,6 +233,7 @@ struct tls_connection { EVP_PKEY *private_key; /* the private key if using engine */ #endif /* OPENSSL_NO_ENGINE */ char *subject_match, *altsubject_match, *suffix_match, *domain_match; + char *check_cert_subject; int read_alerts, write_alerts, failed; tls_session_ticket_cb session_ticket_cb; @@ -301,6 +311,36 @@ static void tls_show_errors(int level, const char *func, const char *txt) #endif /* CONFIG_NO_STDOUT_DEBUG */ +static X509_STORE * tls_crl_cert_reload(const char *ca_cert, int check_crl) +{ + int flags; + X509_STORE *store; + + store = X509_STORE_new(); + if (!store) { + wpa_printf(MSG_DEBUG, + "OpenSSL: %s - failed to allocate new certificate store", + __func__); + return NULL; + } + + if (ca_cert && X509_STORE_load_locations(store, ca_cert, NULL) != 1) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to load root certificates"); + X509_STORE_free(store); + return NULL; + } + + flags = check_crl ? X509_V_FLAG_CRL_CHECK : 0; + if (check_crl == 2) + flags |= X509_V_FLAG_CRL_CHECK_ALL; + + X509_STORE_set_flags(store, flags); + + return store; +} + + #ifdef CONFIG_NATIVE_WINDOWS /* Windows CryptoAPI and access to certificate stores */ @@ -989,8 +1029,10 @@ void * tls_init(const struct tls_config *conf) return NULL; } data->ssl = ssl; - if (conf) + if (conf) { data->tls_session_lifetime = conf->tls_session_lifetime; + data->crl_reload_interval = conf->crl_reload_interval; + } SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3); @@ -1072,6 +1114,7 @@ void tls_deinit(void *ssl_ctx) os_free(context); if (data->tls_session_lifetime > 0) SSL_CTX_flush_sessions(ssl, 0); + os_free(data->ca_cert); SSL_CTX_free(ssl); tls_openssl_ref_count--; @@ -1093,6 +1136,7 @@ void tls_deinit(void *ssl_ctx) tls_global = NULL; } + os_free(data->check_cert_subject); os_free(data); } @@ -1305,8 +1349,16 @@ static const char * openssl_handshake_type(int content_type, const u8 *buf, return "client hello"; case 2: return "server hello"; + case 3: + return "hello verify request"; case 4: return "new session ticket"; + case 5: + return "end of early data"; + case 6: + return "hello retry request"; + case 8: + return "encrypted extensions"; case 11: return "certificate"; case 12: @@ -1325,6 +1377,12 @@ static const char * openssl_handshake_type(int content_type, const u8 *buf, return "certificate url"; case 22: return "certificate status"; + case 23: + return "supplemental data"; + case 24: + return "key update"; + case 254: + return "message hash"; default: return "?"; } @@ -1467,11 +1525,32 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) SSL_CTX *ssl = data->ssl; struct tls_connection *conn; long options; + X509_STORE *new_cert_store; + struct os_reltime now; struct tls_context *context = SSL_CTX_get_app_data(ssl); + /* Replace X509 store if it is time to update CRL. */ + if (data->crl_reload_interval > 0 && os_get_reltime(&now) == 0 && + os_reltime_expired(&now, &data->crl_last_reload, + data->crl_reload_interval)) { + wpa_printf(MSG_INFO, + "OpenSSL: Flushing X509 store with ca_cert file"); + new_cert_store = tls_crl_cert_reload(data->ca_cert, + data->check_crl); + if (!new_cert_store) { + wpa_printf(MSG_ERROR, + "OpenSSL: Error replacing X509 store with ca_cert file"); + } else { + /* Replace old store */ + SSL_CTX_set_cert_store(ssl, new_cert_store); + data->crl_last_reload = now; + } + } + conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; + conn->data = data; conn->ssl_ctx = ssl; conn->ssl = SSL_new(ssl); if (conn->ssl == NULL) { @@ -1535,6 +1614,7 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) os_free(conn->altsubject_match); os_free(conn->suffix_match); os_free(conn->domain_match); + os_free(conn->check_cert_subject); os_free(conn->session_ticket); os_free(conn); } @@ -1655,9 +1735,9 @@ static int tls_match_altsubject(X509 *cert, const char *match) #ifndef CONFIG_NATIVE_WINDOWS static int domain_suffix_match(const u8 *val, size_t len, const char *match, - int full) + size_t match_len, int full) { - size_t i, match_len; + size_t i; /* Check for embedded nuls that could mess up suffix matching */ for (i = 0; i < len; i++) { @@ -1667,7 +1747,6 @@ static int domain_suffix_match(const u8 *val, size_t len, const char *match, } } - match_len = os_strlen(match); if (match_len > len || (full && match_len != len)) return 0; @@ -1687,12 +1766,223 @@ static int domain_suffix_match(const u8 *val, size_t len, const char *match, #endif /* CONFIG_NATIVE_WINDOWS */ -static int tls_match_suffix(X509 *cert, const char *match, int full) +struct tls_dn_field_order_cnt { + u8 cn; + u8 c; + u8 l; + u8 st; + u8 o; + u8 ou; + u8 email; +}; + + +static int get_dn_field_index(const struct tls_dn_field_order_cnt *dn_cnt, + int nid) +{ + switch (nid) { + case NID_commonName: + return dn_cnt->cn; + case NID_countryName: + return dn_cnt->c; + case NID_localityName: + return dn_cnt->l; + case NID_stateOrProvinceName: + return dn_cnt->st; + case NID_organizationName: + return dn_cnt->o; + case NID_organizationalUnitName: + return dn_cnt->ou; + case NID_pkcs9_emailAddress: + return dn_cnt->email; + default: + wpa_printf(MSG_ERROR, + "TLS: Unknown NID '%d' in check_cert_subject", + nid); + return -1; + } +} + + +/** + * match_dn_field - Match configuration DN field against Certificate DN field + * @cert: Certificate + * @nid: NID of DN field + * @field: Field name + * @value DN field value which is passed from configuration + * e.g., if configuration have C=US and this argument will point to US. + * @dn_cnt: DN matching context + * Returns: 1 on success and 0 on failure + */ +static int match_dn_field(const X509 *cert, int nid, const char *field, + const char *value, + const struct tls_dn_field_order_cnt *dn_cnt) +{ + int i, ret = 0, len, config_dn_field_index, match_index = 0; + X509_NAME *name; + + len = os_strlen(value); + name = X509_get_subject_name((X509 *) cert); + + /* Assign incremented cnt for every field of DN to check DN field in + * right order */ + config_dn_field_index = get_dn_field_index(dn_cnt, nid); + if (config_dn_field_index < 0) + return 0; + + /* Fetch value based on NID */ + for (i = -1; (i = X509_NAME_get_index_by_NID(name, nid, i)) > -1;) { + X509_NAME_ENTRY *e; + ASN1_STRING *cn; + + e = X509_NAME_get_entry(name, i); + if (!e) + continue; + + cn = X509_NAME_ENTRY_get_data(e); + if (!cn) + continue; + + match_index++; + + /* check for more than one DN field with same name */ + if (match_index != config_dn_field_index) + continue; + + /* Check wildcard at the right end side */ + /* E.g., if OU=develop* mentioned in configuration, allow 'OU' + * of the subject in the client certificate to start with + * 'develop' */ + if (len > 0 && value[len - 1] == '*') { + /* Compare actual certificate DN field value with + * configuration DN field value up to the specified + * length. */ + ret = ASN1_STRING_length(cn) >= len - 1 && + os_memcmp(ASN1_STRING_get0_data(cn), value, + len - 1) == 0; + } else { + /* Compare actual certificate DN field value with + * configuration DN field value */ + ret = ASN1_STRING_length(cn) == len && + os_memcmp(ASN1_STRING_get0_data(cn), value, + len) == 0; + } + if (!ret) { + wpa_printf(MSG_ERROR, + "OpenSSL: Failed to match %s '%s' with certificate DN field value '%s'", + field, value, ASN1_STRING_get0_data(cn)); + } + break; + } + + return ret; +} + + +/** + * get_value_from_field - Get value from DN field + * @cert: Certificate + * @field_str: DN field string which is passed from configuration file (e.g., + * C=US) + * @dn_cnt: DN matching context + * Returns: 1 on success and 0 on failure + */ +static int get_value_from_field(const X509 *cert, char *field_str, + struct tls_dn_field_order_cnt *dn_cnt) +{ + int nid; + char *context = NULL, *name, *value; + + if (os_strcmp(field_str, "*") == 0) + return 1; /* wildcard matches everything */ + + name = str_token(field_str, "=", &context); + if (!name) + return 0; + + /* Compare all configured DN fields and assign nid based on that to + * fetch correct value from certificate subject */ + if (os_strcmp(name, "CN") == 0) { + nid = NID_commonName; + dn_cnt->cn++; + } else if(os_strcmp(name, "C") == 0) { + nid = NID_countryName; + dn_cnt->c++; + } else if (os_strcmp(name, "L") == 0) { + nid = NID_localityName; + dn_cnt->l++; + } else if (os_strcmp(name, "ST") == 0) { + nid = NID_stateOrProvinceName; + dn_cnt->st++; + } else if (os_strcmp(name, "O") == 0) { + nid = NID_organizationName; + dn_cnt->o++; + } else if (os_strcmp(name, "OU") == 0) { + nid = NID_organizationalUnitName; + dn_cnt->ou++; + } else if (os_strcmp(name, "emailAddress") == 0) { + nid = NID_pkcs9_emailAddress; + dn_cnt->email++; + } else { + wpa_printf(MSG_ERROR, + "TLS: Unknown field '%s' in check_cert_subject", name); + return 0; + } + + value = str_token(field_str, "=", &context); + if (!value) { + wpa_printf(MSG_ERROR, + "TLS: Distinguished Name field '%s' value is not defined in check_cert_subject", + name); + return 0; + } + + return match_dn_field(cert, nid, name, value, dn_cnt); +} + + +/** + * tls_match_dn_field - Match subject DN field with check_cert_subject + * @cert: Certificate + * @match: check_cert_subject string + * Returns: Return 1 on success and 0 on failure +*/ +static int tls_match_dn_field(X509 *cert, const char *match) +{ + const char *token, *last = NULL; + char field[256]; + struct tls_dn_field_order_cnt dn_cnt; + + os_memset(&dn_cnt, 0, sizeof(dn_cnt)); + + /* Maximum length of each DN field is 255 characters */ + + /* Process each '/' delimited field */ + while ((token = cstr_token(match, "/", &last))) { + if (last - token >= (int) sizeof(field)) { + wpa_printf(MSG_ERROR, + "OpenSSL: Too long DN matching field value in '%s'", + match); + return 0; + } + os_memcpy(field, token, last - token); + field[last - token] = '\0'; + + if (!get_value_from_field(cert, field, &dn_cnt)) { + wpa_printf(MSG_DEBUG, "OpenSSL: No match for DN '%s'", + field); + return 0; + } + } + + return 1; +} + + +#ifndef CONFIG_NATIVE_WINDOWS +static int tls_match_suffix_helper(X509 *cert, const char *match, + size_t match_len, int full) { -#ifdef CONFIG_NATIVE_WINDOWS - /* wincrypt.h has conflicting X509_NAME definition */ - return -1; -#else /* CONFIG_NATIVE_WINDOWS */ GENERAL_NAME *gen; void *ext; int i; @@ -1714,8 +2004,8 @@ static int tls_match_suffix(X509 *cert, const char *match, int full) gen->d.dNSName->data, gen->d.dNSName->length); if (domain_suffix_match(gen->d.dNSName->data, - gen->d.dNSName->length, match, full) == - 1) { + gen->d.dNSName->length, + match, match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found", full ? "Match" : "Suffix match"); sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); @@ -1746,8 +2036,8 @@ static int tls_match_suffix(X509 *cert, const char *match, int full) continue; wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", cn->data, cn->length); - if (domain_suffix_match(cn->data, cn->length, match, full) == 1) - { + if (domain_suffix_match(cn->data, cn->length, + match, match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in commonName found", full ? "Match" : "Suffix match"); return 1; @@ -1757,6 +2047,25 @@ static int tls_match_suffix(X509 *cert, const char *match, int full) wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found", full ? "": "suffix "); return 0; +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static int tls_match_suffix(X509 *cert, const char *match, int full) +{ +#ifdef CONFIG_NATIVE_WINDOWS + /* wincrypt.h has conflicting X509_NAME definition */ + return -1; +#else /* CONFIG_NATIVE_WINDOWS */ + const char *token, *last = NULL; + + /* Process each match alternative separately until a match is found */ + while ((token = cstr_token(match, ";", &last))) { + if (tls_match_suffix_helper(cert, token, last - token, full)) + return 1; + } + + return 0; #endif /* CONFIG_NATIVE_WINDOWS */ } @@ -1951,6 +2260,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) struct tls_connection *conn; struct tls_context *context; char *match, *altmatch, *suffix_match, *domain_match; + const char *check_cert_subject; const char *err_str; err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); @@ -1991,6 +2301,13 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) "time mismatch"); preverify_ok = 1; } + if (!preverify_ok && !conn->data->check_crl_strict && + (err == X509_V_ERR_CRL_HAS_EXPIRED || + err == X509_V_ERR_CRL_NOT_YET_VALID)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Ignore certificate validity CRL time mismatch"); + preverify_ok = 1; + } err_str = X509_verify_cert_error_string(err); @@ -2044,6 +2361,18 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'", preverify_ok, err, err_str, conn->ca_cert_verify, depth, buf); + check_cert_subject = conn->check_cert_subject; + if (!check_cert_subject) + check_cert_subject = conn->data->check_cert_subject; + if (check_cert_subject) { + if (depth == 0 && + !tls_match_dn_field(err_cert, check_cert_subject)) { + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Distinguished Name", + TLS_FAIL_DN_MISMATCH); + } + } if (depth == 0 && match && os_strstr(buf, match) == NULL) { wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " "match with '%s'", buf, match); @@ -2381,13 +2710,16 @@ static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert) SSL_CTX_set_client_CA_list(ssl_ctx, SSL_load_client_CA_file(ca_cert)); #endif /* OPENSSL_NO_STDIO */ + + os_free(data->ca_cert); + data->ca_cert = os_strdup(ca_cert); } return 0; } -int tls_global_set_verify(void *ssl_ctx, int check_crl) +int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict) { int flags; @@ -2404,6 +2736,10 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl) if (check_crl == 2) flags |= X509_V_FLAG_CRL_CHECK_ALL; X509_STORE_set_flags(cs, flags); + + data->check_crl = check_crl; + data->check_crl_strict = strict; + os_get_reltime(&data->crl_last_reload); } return 0; } @@ -2413,7 +2749,8 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, const char *subject_match, const char *altsubject_match, const char *suffix_match, - const char *domain_match) + const char *domain_match, + const char *check_cert_subject) { os_free(conn->subject_match); conn->subject_match = NULL; @@ -2447,6 +2784,14 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, return -1; } + os_free(conn->check_cert_subject); + conn->check_cert_subject = NULL; + if (check_cert_subject) { + conn->check_cert_subject = os_strdup(check_cert_subject); + if (!conn->check_cert_subject) + return -1; + } + return 0; } @@ -2519,6 +2864,38 @@ static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags, else SSL_clear_options(ssl, SSL_OP_NO_TLSv1_3); #endif /* SSL_OP_NO_TLSv1_3 */ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (flags & (TLS_CONN_ENABLE_TLSv1_0 | + TLS_CONN_ENABLE_TLSv1_1 | + TLS_CONN_ENABLE_TLSv1_2)) { + int version = 0; + + /* Explicit request to enable TLS versions even if needing to + * override systemwide policies. */ + if (flags & TLS_CONN_ENABLE_TLSv1_0) { + version = TLS1_VERSION; + } else if (flags & TLS_CONN_ENABLE_TLSv1_1) { + if (!(flags & TLS_CONN_DISABLE_TLSv1_0)) + version = TLS1_1_VERSION; + } else if (flags & TLS_CONN_ENABLE_TLSv1_2) { + if (!(flags & (TLS_CONN_DISABLE_TLSv1_0 | + TLS_CONN_DISABLE_TLSv1_1))) + version = TLS1_2_VERSION; + } + if (!version) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Invalid TLS version configuration"); + return -1; + } + + if (SSL_set_min_proto_version(ssl, version) != 1) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Failed to set minimum TLS version"); + return -1; + } + } +#endif /* >= 1.1.0 */ + #ifdef CONFIG_SUITEB #ifdef OPENSSL_IS_BORINGSSL /* Start with defaults from BoringSSL */ @@ -2621,7 +2998,22 @@ static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags, return -1; } } +#else /* OPENSSL_IS_BORINGSSL */ + if (!(flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) && + openssl_ciphers && SSL_set_cipher_list(ssl, openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set openssl_ciphers '%s'", + openssl_ciphers); + return -1; + } #endif /* OPENSSL_IS_BORINGSSL */ +#else /* CONFIG_SUITEB */ + if (openssl_ciphers && SSL_set_cipher_list(ssl, openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set openssl_ciphers '%s'", + openssl_ciphers); + return -1; + } #endif /* CONFIG_SUITEB */ return 0; @@ -2743,6 +3135,15 @@ static int tls_connection_client_cert(struct tls_connection *conn, return 0; } +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL) + if (SSL_use_certificate_chain_file(conn->ssl, client_cert) == 1) { + ERR_clear_error(); + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_chain_file" + " --> OK"); + return 0; + } +#else if (SSL_use_certificate_file(conn->ssl, client_cert, SSL_FILETYPE_PEM) == 1) { ERR_clear_error(); @@ -2750,6 +3151,7 @@ static int tls_connection_client_cert(struct tls_connection *conn, " --> OK"); return 0; } +#endif tls_show_errors(MSG_DEBUG, __func__, "SSL_use_certificate_file failed"); @@ -3523,11 +3925,13 @@ static int openssl_get_keyblock_size(SSL *ssl) int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { if (!conn || SSL_export_keying_material(conn->ssl, out, out_len, label, - os_strlen(label), NULL, 0, 0) != 1) + os_strlen(label), context, context_len, + context != NULL) != 1) return -1; return 0; } @@ -4445,7 +4849,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, params->subject_match, params->altsubject_match, params->suffix_match, - params->domain_match)) + params->domain_match, + params->check_cert_subject)) return -1; if (engine_id && ca_cert_id) { @@ -4503,6 +4908,40 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } + if (!params->openssl_ecdh_curves) { +#ifndef OPENSSL_IS_BORINGSSL +#ifndef OPENSSL_NO_EC +#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) && \ + (OPENSSL_VERSION_NUMBER < 0x10100000L) + if (SSL_set_ecdh_auto(conn->ssl, 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH curves to auto"); + return -1; + } +#endif /* >= 1.0.2 && < 1.1.0 */ +#endif /* OPENSSL_NO_EC */ +#endif /* OPENSSL_IS_BORINGSSL */ + } else if (params->openssl_ecdh_curves[0]) { +#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER < 0x10002000L) + wpa_printf(MSG_INFO, + "OpenSSL: ECDH configuration nnot supported"); + return -1; +#else /* OPENSSL_IS_BORINGSSL || < 1.0.2 */ +#ifndef OPENSSL_NO_EC + if (SSL_set1_curves_list(conn->ssl, + params->openssl_ecdh_curves) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH curves '%s'", + params->openssl_ecdh_curves); + return -1; + } +#else /* OPENSSL_NO_EC */ + wpa_printf(MSG_INFO, "OpenSSL: ECDH not supported"); + return -1; +#endif /* OPENSSL_NO_EC */ +#endif /* OPENSSL_IS_BORINGSSL */ + } + if (tls_set_conn_flags(conn, params->flags, params->openssl_ciphers) < 0) return -1; @@ -4552,6 +4991,15 @@ int tls_global_set_params(void *tls_ctx, __func__, ERR_error_string(err, NULL)); } + os_free(data->check_cert_subject); + data->check_cert_subject = NULL; + if (params->check_cert_subject) { + data->check_cert_subject = + os_strdup(params->check_cert_subject); + if (!data->check_cert_subject) + return -1; + } + 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, @@ -4569,6 +5017,44 @@ int tls_global_set_params(void *tls_ctx, return -1; } + if (!params->openssl_ecdh_curves) { +#ifndef OPENSSL_IS_BORINGSSL +#ifndef OPENSSL_NO_EC +#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) && \ + (OPENSSL_VERSION_NUMBER < 0x10100000L) + if (SSL_CTX_set_ecdh_auto(ssl_ctx, 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH curves to auto"); + return -1; + } +#endif /* >= 1.0.2 && < 1.1.0 */ +#endif /* OPENSSL_NO_EC */ +#endif /* OPENSSL_IS_BORINGSSL */ + } else if (params->openssl_ecdh_curves[0]) { +#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER < 0x10002000L) + wpa_printf(MSG_INFO, + "OpenSSL: ECDH configuration nnot supported"); + return -1; +#else /* OPENSSL_IS_BORINGSSL || < 1.0.2 */ +#ifndef OPENSSL_NO_EC +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSL_CTX_set_ecdh_auto(ssl_ctx, 1); +#endif + if (SSL_CTX_set1_curves_list(ssl_ctx, + params->openssl_ecdh_curves) != + 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH curves '%s'", + params->openssl_ecdh_curves); + return -1; + } +#else /* OPENSSL_NO_EC */ + wpa_printf(MSG_INFO, "OpenSSL: ECDH not supported"); + return -1; +#endif /* OPENSSL_NO_EC */ +#endif /* OPENSSL_IS_BORINGSSL */ + } + #ifdef SSL_OP_NO_TICKET if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET); diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index cc8c704466d2b..e9cb425c115a0 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -643,9 +643,9 @@ static int tls_match_alt_subject(WOLFSSL_X509 *cert, const char *match) static int domain_suffix_match(const char *val, size_t len, const char *match, - int full) + size_t match_len, int full) { - size_t i, match_len; + size_t i; /* Check for embedded nuls that could mess up suffix matching */ for (i = 0; i < len; i++) { @@ -656,7 +656,6 @@ static int domain_suffix_match(const char *val, size_t len, const char *match, } } - match_len = os_strlen(match); if (match_len > len || (full && match_len != len)) return 0; @@ -674,7 +673,8 @@ static int domain_suffix_match(const char *val, size_t len, const char *match, } -static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) +static int tls_match_suffix_helper(WOLFSSL_X509 *cert, const char *match, + size_t match_len, int full) { WOLFSSL_ASN1_OBJECT *gen; void *ext; @@ -690,14 +690,14 @@ static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) for (j = 0; ext && j < wolfSSL_sk_num(ext); j++) { gen = wolfSSL_sk_value(ext, j); - if (gen->type != ALT_NAMES_OID) + if (gen->type != ASN_DNS_TYPE) continue; dns_name++; wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName", gen->obj, os_strlen((char *)gen->obj)); if (domain_suffix_match((const char *) gen->obj, os_strlen((char *) gen->obj), match, - full) == 1) { + match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found", full ? "Match" : "Suffix match"); wolfSSL_sk_ASN1_OBJECT_free(ext); @@ -729,8 +729,8 @@ static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) continue; wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", cn->data, cn->length); - if (domain_suffix_match(cn->data, cn->length, match, full) == 1) - { + if (domain_suffix_match(cn->data, cn->length, + match, match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in commonName found", full ? "Match" : "Suffix match"); return 1; @@ -743,6 +743,20 @@ static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) } +static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) +{ + const char *token, *last = NULL; + + /* Process each match alternative separately until a match is found */ + while ((token = cstr_token(match, ";", &last))) { + if (tls_match_suffix_helper(cert, token, last - token, full)) + return 1; + } + + return 0; +} + + static enum tls_fail_reason wolfssl_tls_fail_reason(int err) { switch (err) { @@ -1487,6 +1501,9 @@ int tls_global_set_params(void *tls_ctx, { wpa_printf(MSG_DEBUG, "SSL: global set params"); + if (params->check_cert_subject) + return -1; /* not yet supported */ + if (tls_global_ca_cert(tls_ctx, params->ca_cert) < 0) { wpa_printf(MSG_INFO, "SSL: Failed to load ca cert file '%s'", params->ca_cert); @@ -1524,6 +1541,12 @@ int tls_global_set_params(void *tls_ctx, return -1; } + if (params->openssl_ecdh_curves) { + wpa_printf(MSG_INFO, + "wolfSSL: openssl_ecdh_curves not supported"); + return -1; + } + #ifdef HAVE_SESSION_TICKET /* Session ticket is off by default - can't disable once on. */ if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET)) @@ -1543,7 +1566,7 @@ int tls_global_set_params(void *tls_ctx, } -int tls_global_set_verify(void *tls_ctx, int check_crl) +int tls_global_set_verify(void *tls_ctx, int check_crl, int strict) { wpa_printf(MSG_DEBUG, "SSL: global set verify: %d", check_crl); @@ -1964,8 +1987,11 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { + if (context) + return -1; if (!conn || wolfSSL_make_eap_keys(conn->ssl, out, out_len, label) != 0) return -1; return 0; diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 4ac9f16a0efcf..e7c8f318f35d7 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -58,6 +58,16 @@ #define HOSTAPD_CHAN_VHT_130_30 0x04000000 #define HOSTAPD_CHAN_VHT_150_10 0x08000000 +/* Allowed bandwidth mask */ +enum hostapd_chan_width_attr { + HOSTAPD_CHAN_WIDTH_10 = BIT(0), + HOSTAPD_CHAN_WIDTH_20 = BIT(1), + HOSTAPD_CHAN_WIDTH_40P = BIT(2), + HOSTAPD_CHAN_WIDTH_40M = BIT(3), + HOSTAPD_CHAN_WIDTH_80 = BIT(4), + HOSTAPD_CHAN_WIDTH_160 = BIT(5), +}; + /* Filter gratuitous ARP */ #define WPA_DATA_FRAME_FILTER_FLAG_ARP BIT(0) /* Filter unsolicited Neighbor Advertisement */ @@ -111,6 +121,13 @@ struct hostapd_channel_data { int flag; /** + * allowed_bw - Allowed channel width bitmask + * + * See enum hostapd_chan_width_attr. + */ + u32 allowed_bw; + + /** * max_tx_power - Regulatory transmit power limit in dBm */ u8 max_tx_power; @@ -914,10 +931,10 @@ struct wpa_driver_associate_params { * passphrase - RSN passphrase for PSK * * This value is made available only for WPA/WPA2-Personal (PSK) and - * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is - * the 8..63 character ASCII passphrase, if available. Please note that - * this can be %NULL if passphrase was not used to generate the PSK. In - * that case, the psk field must be used to fetch the PSK. + * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK. This + * is the 8..63 character ASCII passphrase, if available. Please note + * that this can be %NULL if passphrase was not used to generate the + * PSK. In that case, the psk field must be used to fetch the PSK. */ const char *passphrase; @@ -925,9 +942,9 @@ struct wpa_driver_associate_params { * psk - RSN PSK (alternative for passphrase for PSK) * * This value is made available only for WPA/WPA2-Personal (PSK) and - * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is - * the 32-octet (256-bit) PSK, if available. The driver wrapper should - * be prepared to handle %NULL value as an error. + * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK. This + * is the 32-octet (256-bit) PSK, if available. The driver wrapper + * should be prepared to handle %NULL value as an error. */ const u8 *psk; @@ -1367,6 +1384,23 @@ struct wpa_driver_ap_params { * service). */ int multicast_to_unicast; + + /** + * ftm_responder - Whether FTM responder is enabled + */ + int ftm_responder; + + /** + * lci - Binary data, the content of an LCI report IE with type 8 as + * defined in IEEE Std 802.11-2016, 9.4.2.22.10 + */ + const struct wpabuf *lci; + + /** + * civic - Binary data, the content of a measurement report IE with + * type 11 as defined in IEEE Std 802.11-2016, 9.4.2.22.13 + */ + const struct wpabuf *civic; }; struct wpa_driver_mesh_bss_params { @@ -1424,6 +1458,7 @@ struct wpa_driver_capa { #define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 0x00002000 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 0x00004000 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 0x00008000 +#define WPA_DRIVER_CAPA_KEY_MGMT_SAE 0x00010000 /** Bitfield of supported key management suites */ unsigned int key_mgmt; @@ -1457,7 +1492,7 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_DFS_OFFLOAD 0x00000004 /** Driver takes care of RSN 4-way handshake internally; PMK is configured with * struct wpa_driver_ops::set_key using alg = WPA_ALG_PMK */ -#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE 0x00000008 +#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X 0x00000008 /** Driver is for a wired Ethernet interface */ #define WPA_DRIVER_FLAGS_WIRED 0x00000010 /** Driver provides separate commands for authentication and association (SME in @@ -1579,6 +1614,10 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_MFP_OPTIONAL 0x0040000000000000ULL /** Driver is a self-managed regulatory device */ #define WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY 0x0080000000000000ULL +/** Driver supports FTM responder functionality */ +#define WPA_DRIVER_FLAGS_FTM_RESPONDER 0x0100000000000000ULL +/** Driver support 4-way handshake offload for WPA-Personal */ +#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK 0x0200000000000000ULL u64 flags; #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \ @@ -1902,17 +1941,6 @@ enum smps_mode { SMPS_INVALID, }; -/* enum chan_width - Channel width definitions */ -enum chan_width { - CHAN_WIDTH_20_NOHT, - CHAN_WIDTH_20, - CHAN_WIDTH_40, - CHAN_WIDTH_80, - CHAN_WIDTH_80P80, - CHAN_WIDTH_160, - CHAN_WIDTH_UNKNOWN -}; - #define WPA_INVALID_NOISE 9999 /** @@ -1943,6 +1971,26 @@ struct wpa_signal_info { }; /** + * struct wpa_channel_info - Information about the current channel + * @frequency: Center frequency of the primary 20 MHz channel + * @chanwidth: Width of the current operating channel + * @sec_channel: Location of the secondary 20 MHz channel (either +1 or -1). + * This field is only filled in when using a 40 MHz channel. + * @center_frq1: Center frequency of frequency segment 0 + * @center_frq2: Center frequency of frequency segment 1 (for 80+80 channels) + * @seg1_idx: Frequency segment 1 index when using a 80+80 channel. This is + * derived from center_frq2 for convenience. + */ +struct wpa_channel_info { + u32 frequency; + enum chan_width chanwidth; + int sec_channel; + int center_frq1; + int center_frq2; + u8 seg1_idx; +}; + +/** * struct beacon_data - Beacon data * @head: Head portion of Beacon frame (before TIM IE) * @tail: Tail portion of Beacon frame (after TIM IE) @@ -2108,17 +2156,19 @@ enum wpa_drv_update_connect_params_mask { * use %WLAN_STATUS_UNSPECIFIED_FAILURE if wpa_supplicant cannot give * the real status code for failures. Used only for the request interface * from user space to the driver. + * @pmkid: Generated PMKID as part of external auth exchange (e.g., SAE). */ struct external_auth { enum { EXT_AUTH_START, EXT_AUTH_ABORT, } action; - u8 bssid[ETH_ALEN]; - u8 ssid[SSID_MAX_LEN]; + const u8 *bssid; + const u8 *ssid; size_t ssid_len; unsigned int key_mgmt_suite; u16 status; + const u8 *pmkid; }; /** @@ -3378,6 +3428,14 @@ struct wpa_driver_ops { int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info); /** + * channel_info - Get parameters of the current operating channel + * @priv: Private driver interface data + * @channel_info: Channel info structure + * Returns: 0 on success, negative (<0) on failure + */ + int (*channel_info)(void *priv, struct wpa_channel_info *channel_info); + + /** * set_authmode - Set authentication algorithm(s) for static WEP * @priv: Private driver interface data * @authmode: 1=Open System, 2=Shared Key, 3=both @@ -3658,7 +3716,7 @@ struct wpa_driver_ops { /** * status - Get driver interface status information * @priv: Private driver interface data - * @buf: Buffer for printing tou the status information + * @buf: Buffer for printing the status information * @buflen: Maximum length of the buffer * Returns: Length of written status information or -1 on failure */ @@ -3782,6 +3840,14 @@ struct wpa_driver_ops { int (*set_transmit_next_pn)(void *priv, struct transmit_sa *sa); /** + * set_receive_lowest_pn - Set receive lowest PN + * @priv: Private driver interface data + * @sa: secure association + * Returns: 0 on success, -1 on failure (or if not supported) + */ + int (*set_receive_lowest_pn)(void *priv, struct receive_sa *sa); + + /** * create_receive_sc - create secure channel for receiving * @priv: Private driver interface data * @sc: secure channel @@ -4092,6 +4158,15 @@ struct wpa_driver_ops { */ int (*send_external_auth_status)(void *priv, struct external_auth *params); + + /** + * set_4addr_mode - Set 4-address mode + * @priv: Private driver interface data + * @bridge_ifname: Bridge interface name + * @val: 0 - disable 4addr mode, 1 - enable 4addr mode + * Returns: 0 on success, < 0 on failure + */ + int (*set_4addr_mode)(void *priv, const char *bridge_ifname, int val); }; /** @@ -5534,6 +5609,8 @@ const char * event_to_string(enum wpa_event_type event); /* Convert chan_width to a string for logging and control interfaces */ const char * channel_width_to_string(enum chan_width width); +int channel_width_to_int(enum chan_width width); + int ht_supported(const struct hostapd_hw_modes *mode); int vht_supported(const struct hostapd_hw_modes *mode); diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c index 62f5baad63612..807cd94691d0d 100644 --- a/src/drivers/driver_atheros.c +++ b/src/drivers/driver_atheros.c @@ -1218,8 +1218,7 @@ atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) #ifdef ATH_WPS_IE /* if WPS IE is present, preference is given to WPS */ - if (ie.wps_ie && - (ie.wps_ie[1] > 0 && (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC))) { + if (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC && ie.wps_ie[1] > 0) { iebuf = ie.wps_ie; ielen = ie.wps_ie[1]; } diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c index 8621aa0b0099f..46754968f13d7 100644 --- a/src/drivers/driver_bsd.c +++ b/src/drivers/driver_bsd.c @@ -143,7 +143,7 @@ bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg, ireq->i_data = arg; if (ioctl(drv->global->sock, SIOCG80211, ireq) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, " + wpa_printf(MSG_ERROR, "ioctl[SIOCG80211, op=%u, " "arg_len=%u]: %s", op, arg_len, strerror(errno)); return -1; } diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c index ac0916e4061f9..e55e6cd2b795a 100644 --- a/src/drivers/driver_common.c +++ b/src/drivers/driver_common.c @@ -115,6 +115,25 @@ const char * channel_width_to_string(enum chan_width width) } +int channel_width_to_int(enum chan_width width) +{ + switch (width) { + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + return 20; + case CHAN_WIDTH_40: + return 40; + case CHAN_WIDTH_80: + return 80; + case CHAN_WIDTH_80P80: + case CHAN_WIDTH_160: + return 160; + default: + return 0; + } +} + + int ht_supported(const struct hostapd_hw_modes *mode) { if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) { @@ -234,7 +253,8 @@ const char * driver_flag_to_string(u64 flag) DF2S(DRIVER_IE); DF2S(SET_KEYS_AFTER_ASSOC); DF2S(DFS_OFFLOAD); - DF2S(4WAY_HANDSHAKE); + DF2S(4WAY_HANDSHAKE_PSK); + DF2S(4WAY_HANDSHAKE_8021X); DF2S(WIRED); DF2S(SME); DF2S(AP); diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c index 597da335e4748..61b39b19721c1 100644 --- a/src/drivers/driver_hostap.c +++ b/src/drivers/driver_hostap.c @@ -231,7 +231,11 @@ static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr) } memset(&ifr, 0, sizeof(ifr)); - snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", drv->iface); + if (os_snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", + drv->iface) >= (int) sizeof(ifr.ifr_name)) { + wpa_printf(MSG_ERROR, "hostap: AP interface name truncated"); + return -1; + } if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", strerror(errno)); @@ -348,7 +352,10 @@ static int hostap_set_iface_flags(void *priv, int dev_up) struct ifreq ifr; char ifname[IFNAMSIZ]; - os_snprintf(ifname, IFNAMSIZ, "%sap", drv->iface); + if (os_snprintf(ifname, IFNAMSIZ, "%sap", drv->iface) >= IFNAMSIZ) { + wpa_printf(MSG_ERROR, "hostap: AP interface name truncated"); + return -1; + } if (linux_set_iface_flags(drv->ioctl_sock, ifname, dev_up) < 0) return -1; @@ -1124,6 +1131,7 @@ static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv, for (i = 0; i < 14; i++) { mode->channels[i].chan = i + 1; mode->channels[i].freq = chan2freq[i]; + mode->channels[i].allowed_bw = HOSTAPD_CHAN_WIDTH_20; /* TODO: Get allowed channel list from the driver */ if (i >= 11) mode->channels[i].flag = HOSTAPD_CHAN_DISABLED; diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c index 4f6629f5aaa2b..9d981bb70f78c 100644 --- a/src/drivers/driver_macsec_linux.c +++ b/src/drivers/driver_macsec_linux.c @@ -177,6 +177,9 @@ static int try_commit(struct macsec_drv_data *drv) if (drv->controlled_port_enabled_set) { struct rtnl_link *change = rtnl_link_alloc(); + wpa_printf(MSG_DEBUG, DRV_PREFIX + "%s: try_commit controlled_port_enabled=%d", + drv->ifname, drv->controlled_port_enabled); if (!change) return -1; @@ -196,13 +199,24 @@ static int try_commit(struct macsec_drv_data *drv) drv->controlled_port_enabled_set = FALSE; } - if (drv->protect_frames_set) + if (drv->protect_frames_set) { + wpa_printf(MSG_DEBUG, DRV_PREFIX + "%s: try_commit protect_frames=%d", + drv->ifname, drv->protect_frames); rtnl_link_macsec_set_protect(drv->link, drv->protect_frames); + } - if (drv->encrypt_set) + if (drv->encrypt_set) { + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: try_commit encrypt=%d", + drv->ifname, drv->encrypt); rtnl_link_macsec_set_encrypt(drv->link, drv->encrypt); + } if (drv->replay_protect_set) { + wpa_printf(MSG_DEBUG, DRV_PREFIX + "%s: try_commit replay_protect=%d replay_window=%d", + drv->ifname, drv->replay_protect, + drv->replay_window); rtnl_link_macsec_set_replay_protect(drv->link, drv->replay_protect); if (drv->replay_protect) @@ -210,8 +224,12 @@ static int try_commit(struct macsec_drv_data *drv) drv->replay_window); } - if (drv->encoding_sa_set) + if (drv->encoding_sa_set) { + wpa_printf(MSG_DEBUG, DRV_PREFIX + "%s: try_commit encoding_sa=%d", + drv->ifname, drv->encoding_sa); rtnl_link_macsec_set_encoding_sa(drv->link, drv->encoding_sa); + } err = rtnl_link_add(drv->sk, drv->link, 0); if (err < 0) @@ -318,6 +336,8 @@ static int macsec_drv_macsec_init(void *priv, struct macsec_init_params *params) drv->common.ifname); goto cache; } + wpa_printf(MSG_DEBUG, DRV_PREFIX "ifname=%s parent_ifi=%d", + drv->common.ifname, drv->parent_ifi); err = init_genl_ctx(drv); if (err < 0) @@ -670,6 +690,50 @@ static int macsec_drv_get_receive_lowest_pn(void *priv, struct receive_sa *sa) /** + * macsec_drv_set_receive_lowest_pn - Set receive lowest PN + * @priv: Private driver interface data + * @sa: secure association + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_set_receive_lowest_pn(void *priv, struct receive_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + struct nl_msg *msg; + struct nlattr *nest; + int ret = -1; + + wpa_printf(MSG_DEBUG, + DRV_PREFIX "%s: set_receive_lowest_pn -> %d: %d", + drv->ifname, sa->an, sa->next_pn); + + msg = msg_prepare(MACSEC_CMD_UPD_RXSA, ctx, drv->ifi); + if (!msg) + return ret; + + nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG); + if (!nest) + goto nla_put_failure; + + NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an); + NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn); + + nla_nest_end(msg, nest); + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "failed to communicate: %d (%s)", + ret, nl_geterror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +/** * macsec_drv_get_transmit_next_pn - Get transmit next PN * @priv: Private driver interface data * @sa: secure association @@ -754,8 +818,10 @@ static int macsec_drv_create_receive_sc(void *priv, struct receive_sc *sc, struct nl_msg *msg; int ret = -1; - wpa_printf(MSG_DEBUG, "%s -> " SCISTR, __func__, - SCI2STR(sc->sci.addr, sc->sci.port)); + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: create_receive_sc -> " SCISTR + " (conf_offset=%u validation=%d)", + drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port), + conf_offset, validation); msg = msg_prepare(MACSEC_CMD_ADD_RXSC, ctx, drv->ifi); if (!msg) @@ -790,8 +856,8 @@ static int macsec_drv_delete_receive_sc(void *priv, struct receive_sc *sc) struct nl_msg *msg; int ret = -1; - wpa_printf(MSG_DEBUG, "%s -> " SCISTR, __func__, - SCI2STR(sc->sci.addr, sc->sci.port)); + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_receive_sc -> " SCISTR, + drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port)); msg = msg_prepare(MACSEC_CMD_DEL_RXSC, ctx, drv->ifi); if (!msg) @@ -827,8 +893,17 @@ static int macsec_drv_create_receive_sa(void *priv, struct receive_sa *sa) struct nlattr *nest; int ret = -1; - wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an, - SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); + wpa_printf(MSG_DEBUG, + DRV_PREFIX "%s: create_receive_sa -> %d on " SCISTR + " (enable_receive=%d next_pn=%u)", + drv->ifname, sa->an, + SCI2STR(sa->sc->sci.addr, sa->sc->sci.port), + sa->enable_receive, sa->next_pn); + wpa_hexdump(MSG_DEBUG, DRV_PREFIX "SA keyid", + &sa->pkey->key_identifier, + sizeof(sa->pkey->key_identifier)); + wpa_hexdump_key(MSG_DEBUG, DRV_PREFIX "SA key", + sa->pkey->key, sa->pkey->key_len); msg = msg_prepare(MACSEC_CMD_ADD_RXSA, ctx, drv->ifi); if (!msg) @@ -877,7 +952,8 @@ static int macsec_drv_delete_receive_sa(void *priv, struct receive_sa *sa) struct nlattr *nest; int ret = -1; - wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an, + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_receive_sa -> %d on " + SCISTR, drv->ifname, sa->an, SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); msg = msg_prepare(MACSEC_CMD_DEL_RXSA, ctx, drv->ifi); @@ -954,7 +1030,8 @@ static int macsec_drv_enable_receive_sa(void *priv, struct receive_sa *sa) struct macsec_drv_data *drv = priv; struct macsec_genl_ctx *ctx = &drv->ctx; - wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an, + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: enable_receive_sa -> %d on " + SCISTR, drv->ifname, sa->an, SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci), @@ -973,7 +1050,8 @@ static int macsec_drv_disable_receive_sa(void *priv, struct receive_sa *sa) struct macsec_drv_data *drv = priv; struct macsec_genl_ctx *ctx = &drv->ctx; - wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an, + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: disable_receive_sa -> %d on " + SCISTR, drv->ifname, sa->an, SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci), @@ -1017,7 +1095,10 @@ static int macsec_drv_create_transmit_sc( u64 sci; int err; - wpa_printf(MSG_DEBUG, "%s", __func__); + wpa_printf(MSG_DEBUG, DRV_PREFIX + "%s: create_transmit_sc -> " SCISTR " (conf_offset=%d)", + drv->common.ifname, SCI2STR(sc->sci.addr, sc->sci.port), + conf_offset); if (!drv->sk) { wpa_printf(MSG_ERROR, DRV_PREFIX "NULL rtnl socket"); @@ -1060,6 +1141,9 @@ static int macsec_drv_create_transmit_sc( drv->ifi = rtnl_link_get_ifindex(link); ifname = rtnl_link_get_name(link); + wpa_printf(MSG_DEBUG, + DRV_PREFIX "%s: create_transmit_sc: ifi=%d ifname=%s", + drv->common.ifname, drv->ifi, ifname); os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); rtnl_link_put(link); @@ -1088,7 +1172,8 @@ static int macsec_drv_delete_transmit_sc(void *priv, struct transmit_sc *sc) struct macsec_drv_data *drv = priv; int err; - wpa_printf(MSG_DEBUG, "%s", __func__); + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_transmit_sc -> " SCISTR, + drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port)); if (!drv->sk) return 0; @@ -1125,7 +1210,16 @@ static int macsec_drv_create_transmit_sa(void *priv, struct transmit_sa *sa) struct nlattr *nest; int ret = -1; - wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an); + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: create_transmit_sa -> %d on " + SCISTR " (enable_transmit=%d next_pn=%u)", + drv->ifname, sa->an, + SCI2STR(sa->sc->sci.addr, sa->sc->sci.port), + sa->enable_transmit, sa->next_pn); + wpa_hexdump(MSG_DEBUG, DRV_PREFIX "SA keyid", + &sa->pkey->key_identifier, + sizeof(sa->pkey->key_identifier)); + wpa_hexdump_key(MSG_DEBUG, DRV_PREFIX "SA key", + sa->pkey->key, sa->pkey->key_len); msg = msg_prepare(MACSEC_CMD_ADD_TXSA, ctx, drv->ifi); if (!msg) @@ -1171,7 +1265,9 @@ static int macsec_drv_delete_transmit_sa(void *priv, struct transmit_sa *sa) struct nlattr *nest; int ret = -1; - wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an); + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_transmit_sa -> %d on " + SCISTR, drv->ifname, sa->an, + SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); msg = msg_prepare(MACSEC_CMD_DEL_TXSA, ctx, drv->ifi); if (!msg) @@ -1243,7 +1339,9 @@ static int macsec_drv_enable_transmit_sa(void *priv, struct transmit_sa *sa) struct macsec_genl_ctx *ctx = &drv->ctx; int ret; - wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an); + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: enable_transmit_sa -> %d on " + SCISTR, drv->ifname, sa->an, + SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); ret = set_active_tx_sa(ctx, drv->ifi, sa->an, TRUE); if (ret < 0) { @@ -1269,12 +1367,38 @@ static int macsec_drv_disable_transmit_sa(void *priv, struct transmit_sa *sa) struct macsec_drv_data *drv = priv; struct macsec_genl_ctx *ctx = &drv->ctx; - wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an); + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: disable_transmit_sa -> %d on " + SCISTR, drv->ifname, sa->an, + SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); return set_active_tx_sa(ctx, drv->ifi, sa->an, FALSE); } +static int macsec_drv_status(void *priv, char *buf, size_t buflen) +{ + struct macsec_drv_data *drv = priv; + int res; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + res = os_snprintf(pos, end - pos, + "ifname=%s\n" + "ifi=%d\n" + "parent_ifname=%s\n" + "parent_ifi=%d\n", + drv->common.ifname, drv->ifi, + drv->ifname, drv->parent_ifi); + if (os_snprintf_error(end - pos, res)) + return pos - buf; + pos += res; + + return pos - buf; +} + + const struct wpa_driver_ops wpa_driver_macsec_linux_ops = { .name = "macsec_linux", .desc = "MACsec Ethernet driver for Linux", @@ -1293,6 +1417,7 @@ const struct wpa_driver_ops wpa_driver_macsec_linux_ops = { .set_current_cipher_suite = macsec_drv_set_current_cipher_suite, .enable_controlled_port = macsec_drv_enable_controlled_port, .get_receive_lowest_pn = macsec_drv_get_receive_lowest_pn, + .set_receive_lowest_pn = macsec_drv_set_receive_lowest_pn, .get_transmit_next_pn = macsec_drv_get_transmit_next_pn, .set_transmit_next_pn = macsec_drv_set_transmit_next_pn, .create_receive_sc = macsec_drv_create_receive_sc, @@ -1307,4 +1432,6 @@ const struct wpa_driver_ops wpa_driver_macsec_linux_ops = { .delete_transmit_sa = macsec_drv_delete_transmit_sa, .enable_transmit_sa = macsec_drv_enable_transmit_sa, .disable_transmit_sa = macsec_drv_disable_transmit_sa, + + .status = macsec_drv_status, }; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 871a5d0b4a205..54fe3900096a8 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -40,6 +40,9 @@ #include "driver_nl80211.h" +#ifndef NETLINK_CAP_ACK +#define NETLINK_CAP_ACK 10 +#endif /* NETLINK_CAP_ACK */ /* support for extack if compilation headers are too old */ #ifndef NETLINK_EXT_ACK #define NETLINK_EXT_ACK 11 @@ -304,6 +307,7 @@ void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv) os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); drv->associated = 0; os_memset(drv->bssid, 0, ETH_ALEN); + drv->first_bss->freq = 0; } @@ -406,6 +410,11 @@ static int send_and_recv(struct nl80211_global *global, setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK, NETLINK_EXT_ACK, &opt, sizeof(opt)); + /* try to set NETLINK_CAP_ACK to 1, ignoring errors */ + opt = 1; + setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK, + NETLINK_CAP_ACK, &opt, sizeof(opt)); + err = nl_send_auto_complete(nl_handle, msg); if (err < 0) goto out; @@ -1539,6 +1548,70 @@ int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv, } +static int get_channel_info(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1] = { 0 }; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wpa_channel_info *chan_info = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + os_memset(chan_info, 0, sizeof(struct wpa_channel_info)); + chan_info->chanwidth = CHAN_WIDTH_UNKNOWN; + + if (tb[NL80211_ATTR_WIPHY_FREQ]) + chan_info->frequency = + nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); + if (tb[NL80211_ATTR_CHANNEL_WIDTH]) + chan_info->chanwidth = convert2width( + nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH])); + if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + enum nl80211_channel_type ct = + nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + + switch (ct) { + case NL80211_CHAN_HT40MINUS: + chan_info->sec_channel = -1; + break; + case NL80211_CHAN_HT40PLUS: + chan_info->sec_channel = 1; + break; + default: + chan_info->sec_channel = 0; + break; + } + } + if (tb[NL80211_ATTR_CENTER_FREQ1]) + chan_info->center_frq1 = + nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); + if (tb[NL80211_ATTR_CENTER_FREQ2]) + chan_info->center_frq2 = + nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); + + if (chan_info->center_frq2) { + u8 seg1_idx = 0; + + if (ieee80211_freq_to_chan(chan_info->center_frq2, &seg1_idx) != + NUM_HOSTAPD_MODES) + chan_info->seg1_idx = seg1_idx; + } + + return NL_SKIP; +} + + +static int nl80211_channel_info(void *priv, struct wpa_channel_info *ci) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE); + return send_and_recv_msgs(drv, msg, get_channel_info, ci); +} + + static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, void *handle) { @@ -2169,6 +2242,11 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) ret = -1; #endif /* CONFIG_DPP */ #ifdef CONFIG_IEEE80211W +#ifdef CONFIG_OCV + /* SA Query Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x08\x00", 2) < 0) + ret = -1; +#endif /* CONFIG_OCV */ /* SA Query Response */ if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0) ret = -1; @@ -2392,6 +2470,16 @@ static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss) if (nl80211_action_subscribe_ap(bss)) goto out_err; + if (bss->drv->device_ap_sme) { + u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4); + + /* Register for all Authentication frames */ + if (nl80211_register_frame(bss, bss->nl_mgmt, type, NULL, 0) + < 0) + wpa_printf(MSG_DEBUG, + "nl80211: Failed to subscribe to handle Authentication frames - SAE offload may not work"); + } + nl80211_mgmt_handle_register_eloop(bss); return 0; @@ -2962,7 +3050,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, #endif /* CONFIG_DRIVER_NL80211_QCA */ if (alg == WPA_ALG_PMK && - (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) + (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X)) return nl80211_set_pmk(drv, key, key_len, addr); if (alg == WPA_ALG_NONE) { @@ -3400,8 +3488,8 @@ retry: goto fail; } if (params->ssid) { - wpa_hexdump_ascii(MSG_DEBUG, " * SSID", - params->ssid, params->ssid_len); + wpa_printf(MSG_DEBUG, " * SSID=%s", + wpa_ssid_txt(params->ssid, params->ssid_len)); if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) goto fail; @@ -3968,7 +4056,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; u8 cmd = NL80211_CMD_NEW_BEACON; - int ret; + int ret = -ENOBUFS; int beacon_set; int num_suites; int smps_mode; @@ -3997,8 +4085,8 @@ static int wpa_driver_nl80211_set_ap(void *priv, wpa_printf(MSG_DEBUG, "nl80211: beacon_rate=%u", params->beacon_rate); wpa_printf(MSG_DEBUG, "nl80211: rate_type=%d", params->rate_type); wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period); - wpa_hexdump_ascii(MSG_DEBUG, "nl80211: ssid", - params->ssid, params->ssid_len); + wpa_printf(MSG_DEBUG, "nl80211: ssid=%s", + wpa_ssid_txt(params->ssid, params->ssid_len)); if (!(msg = nl80211_bss_msg(bss, 0, cmd)) || nla_put(msg, NL80211_ATTR_BEACON_HEAD, params->head_len, params->head) || @@ -4083,6 +4171,11 @@ static int wpa_driver_nl80211_set_ap(void *priv, nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))) goto fail; + if (drv->device_ap_sme && + (params->key_mgmt_suites & WPA_KEY_MGMT_SAE) && + nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT)) + goto fail; + wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x", params->pairwise_ciphers); num_suites = wpa_cipher_to_cipher_suites(params->pairwise_ciphers, @@ -4174,6 +4267,29 @@ static int wpa_driver_nl80211_set_ap(void *priv, goto fail; } + if (params->ftm_responder) { + struct nlattr *ftm; + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_FTM_RESPONDER)) { + ret = -ENOTSUP; + goto fail; + } + + ftm = nla_nest_start(msg, NL80211_ATTR_FTM_RESPONDER); + if (!ftm || + nla_put_flag(msg, NL80211_FTM_RESP_ATTR_ENABLED) || + (params->lci && + nla_put(msg, NL80211_FTM_RESP_ATTR_LCI, + wpabuf_len(params->lci), + wpabuf_head(params->lci))) || + (params->civic && + nla_put(msg, NL80211_FTM_RESP_ATTR_CIVICLOC, + wpabuf_len(params->civic), + wpabuf_head(params->civic)))) + goto fail; + nla_nest_end(msg, ftm); + } + ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)", @@ -4225,7 +4341,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, return ret; fail: nlmsg_free(msg); - return -ENOBUFS; + return ret; } @@ -4579,7 +4695,8 @@ static int wpa_driver_nl80211_sta_add(void *priv, goto fail; #endif /* CONFIG_MESH */ - if (params->flags & WPA_STA_WMM) { + if ((!params->set || FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) && + (params->flags & WPA_STA_WMM)) { struct nlattr *wme = nla_nest_start(msg, NL80211_ATTR_STA_WME); wpa_printf(MSG_DEBUG, " * qosinfo=0x%x", params->qosinfo); @@ -5189,8 +5306,8 @@ retry: params->ssid == NULL || params->ssid_len > sizeof(drv->ssid)) goto fail; - wpa_hexdump_ascii(MSG_DEBUG, " * SSID", - params->ssid, params->ssid_len); + wpa_printf(MSG_DEBUG, " * SSID=%s", + wpa_ssid_txt(params->ssid, params->ssid_len)); if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) goto fail; os_memcpy(drv->ssid, params->ssid, params->ssid_len); @@ -5351,8 +5468,8 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, } if (params->ssid) { - wpa_hexdump_ascii(MSG_DEBUG, " * SSID", - params->ssid, params->ssid_len); + wpa_printf(MSG_DEBUG, " * SSID=%s", + wpa_ssid_txt(params->ssid, params->ssid_len)); if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) return -1; @@ -5410,8 +5527,11 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, params->key_mgmt_suite == WPA_KEY_MGMT_OSEN || params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || + params->key_mgmt_suite == WPA_KEY_MGMT_SAE || + params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE || params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B || params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 || + params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 || params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA256 || params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA384 || params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA256 || @@ -5442,12 +5562,21 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, case WPA_KEY_MGMT_OSEN: mgmt = RSN_AUTH_KEY_MGMT_OSEN; break; + case WPA_KEY_MGMT_SAE: + mgmt = RSN_AUTH_KEY_MGMT_SAE; + break; + case WPA_KEY_MGMT_FT_SAE: + mgmt = RSN_AUTH_KEY_MGMT_FT_SAE; + break; case WPA_KEY_MGMT_IEEE8021X_SUITE_B: mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; break; case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; break; + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384; + break; case WPA_KEY_MGMT_FILS_SHA256: mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA256; break; @@ -5476,9 +5605,16 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, return -1; } + if (params->req_key_mgmt_offload && + (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X)) { + wpa_printf(MSG_DEBUG, " * WANT_1X_4WAY_HS"); + if (nla_put_flag(msg, NL80211_ATTR_WANT_1X_4WAY_HS)) + return -1; + } + /* Add PSK in case of 4-way handshake offload */ if (params->psk && - (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) { + (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK)) { wpa_hexdump_key(MSG_DEBUG, " * PSK", params->psk, 32); if (nla_put(msg, NL80211_ATTR_PMK, 32, params->psk)) return -1; @@ -5609,9 +5745,10 @@ skip_auth_type: goto fail; if (nl_connect) - ret = send_and_recv(drv->global, nl_connect, msg, NULL, NULL); + ret = send_and_recv(drv->global, nl_connect, msg, + NULL, (void *) -1); else - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1); msg = NULL; if (ret) { @@ -5623,6 +5760,7 @@ skip_auth_type: } fail: + nl80211_nlmsg_clear(msg); nlmsg_free(msg); return ret; @@ -6023,6 +6161,7 @@ static int get_key_handler(struct nl_msg *msg, void *arg) if (tb[NL80211_ATTR_KEY_SEQ]) memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]), min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6)); + nl80211_nlmsg_clear(msg); return NL_SKIP; } @@ -6057,7 +6196,7 @@ static int i802_set_rts(void *priv, int rts) int ret; u32 val; - if (rts >= 2347) + if (rts >= 2347 || rts == -1) val = (u32) -1; else val = rts; @@ -6085,7 +6224,7 @@ static int i802_set_frag(void *priv, int frag) int ret; u32 val; - if (frag >= 2346) + if (frag >= 2346 || frag == -1) val = (u32) -1; else val = frag; @@ -6646,9 +6785,12 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, } return i802_set_sta_vlan(priv, addr, name, 0); } else { - if (bridge_ifname) - linux_br_del_if(drv->global->ioctl_sock, bridge_ifname, - name); + if (bridge_ifname && + linux_br_del_if(drv->global->ioctl_sock, bridge_ifname, + name) < 0) + wpa_printf(MSG_INFO, + "nl80211: Failed to remove interface %s from bridge %s: %s", + name, bridge_ifname, strerror(errno)); i802_set_sta_vlan(priv, addr, bss->ifname, 0); nl80211_remove_iface(drv, if_nametoindex(name)); @@ -7815,13 +7957,15 @@ static int nl80211_pmkid(struct i802_bss *bss, int cmd, (params->fils_cache_id && nla_put(msg, NL80211_ATTR_FILS_CACHE_ID, 2, params->fils_cache_id)) || - (params->pmk_len && params->pmk_len <= PMK_MAX_LEN && + (cmd != NL80211_CMD_DEL_PMKSA && + params->pmk_len && params->pmk_len <= PMK_MAX_LEN && nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) { + nl80211_nlmsg_clear(msg); nlmsg_free(msg); return -ENOBUFS; } - return send_and_recv_msgs(bss->drv, msg, NULL, NULL); + return send_and_recv_msgs(bss->drv, msg, NULL, (void *) -1); } @@ -8144,6 +8288,7 @@ static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; + u64 cookie; int ret; if (!drv->poll_command_supported) { @@ -8157,11 +8302,16 @@ static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, return; } - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); if (ret < 0) { wpa_printf(MSG_DEBUG, "nl80211: Client probe request for " MACSTR " failed: ret=%d (%s)", MAC2STR(addr), ret, strerror(-ret)); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Client probe request addr=" MACSTR + " cookie=%llu", MAC2STR(addr), + (long long unsigned int) cookie); } } @@ -8593,6 +8743,8 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) struct wpa_driver_nl80211_data *drv = bss->drv; int res; char *pos, *end; + struct nl_msg *msg; + char alpha2[3] = { 0, 0, 0 }; pos = buf; end = buf + buflen; @@ -8737,6 +8889,23 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) pos += res; } + msg = nlmsg_alloc(); + if (msg && + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG) && + nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx) == 0) { + if (send_and_recv_msgs(drv, msg, nl80211_get_country, + alpha2) == 0 && + alpha2[0]) { + res = os_snprintf(pos, end - pos, "country=%s\n", + alpha2); + if (os_snprintf_error(end - pos, res)) + return pos - buf; + pos += res; + } + } else { + nlmsg_free(msg); + } + return pos - buf; } @@ -9303,8 +9472,8 @@ static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id, size_t mesh_id_len) { if (mesh_id) { - wpa_hexdump_ascii(MSG_DEBUG, " * Mesh ID (SSID)", - mesh_id, mesh_id_len); + wpa_printf(MSG_DEBUG, " * Mesh ID (SSID)=%s", + wpa_ssid_txt(mesh_id, mesh_id_len)); return nla_put(msg, NL80211_ATTR_MESH_ID, mesh_id_len, mesh_id); } @@ -10645,15 +10814,27 @@ static int nl80211_send_external_auth_status(void *priv, struct nl_msg *msg = NULL; int ret = -1; + /* External auth command/status is intended for drivers that implement + * intenral SME but want to offload authentication processing (e.g., + * SAE) to hostapd/wpa_supplicant. Do nott send the status to drivers + * which do not support AP SME or use wpa_supplicant/hostapd SME. + */ + if (!bss->drv->device_ap_sme || + (drv->capa.flags & WPA_DRIVER_FLAGS_SME)) + return -1; + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: External auth status: %u", params->status); msg = nl80211_drv_msg(drv, 0, NL80211_CMD_EXTERNAL_AUTH); if (!msg || nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, params->status) || - nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, - params->ssid) || - nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid)) + (params->ssid && params->ssid_len && + nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) || + (params->pmkid && + nla_put(msg, NL80211_ATTR_PMKID, PMKID_LEN, params->pmkid)) || + (params->bssid && + nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid))) goto fail; ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; @@ -10669,6 +10850,49 @@ fail: } +static int nl80211_set_4addr_mode(void *priv, const char *bridge_ifname, + int val) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret = -ENOBUFS; + + wpa_printf(MSG_DEBUG, "nl80211: %s 4addr mode (bridge_ifname: %s)", + val ? "Enable" : "Disable", bridge_ifname); + + msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE); + if (!msg || nla_put_u8(msg, NL80211_ATTR_4ADDR, val)) + goto fail; + + if (bridge_ifname[0] && bss->added_if_into_bridge && !val) { + if (linux_br_del_if(drv->global->ioctl_sock, + bridge_ifname, bss->ifname)) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to remove interface %s from bridge %s", + bss->ifname, bridge_ifname); + return -1; + } + bss->added_if_into_bridge = 0; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (!ret) { + if (bridge_ifname[0] && val && + i802_check_bridge(drv, bss, bridge_ifname, bss->ifname) < 0) + return -1; + return 0; + } + +fail: + nlmsg_free(msg); + wpa_printf(MSG_ERROR, "nl80211: Failed to enable/disable 4addr"); + + return ret; +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -10728,6 +10952,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .resume = wpa_driver_nl80211_resume, .signal_monitor = nl80211_signal_monitor, .signal_poll = nl80211_signal_poll, + .channel_info = nl80211_channel_info, .send_frame = nl80211_send_frame, .set_param = nl80211_set_param, .get_radio_name = nl80211_get_radio_name, @@ -10797,4 +11022,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .get_ext_capab = nl80211_get_ext_capab, .update_connect_params = nl80211_update_connection_params, .send_external_auth_status = nl80211_send_external_auth_status, + .set_4addr_mode = nl80211_set_4addr_mode, }; diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index bc562ba7a8cca..1e7fe7a98fff3 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -166,6 +166,7 @@ struct wpa_driver_nl80211_data { unsigned int he_capab_vendor_cmd_avail:1; unsigned int fetch_bss_trans_status:1; unsigned int roam_vendor_cmd_avail:1; + unsigned int get_supported_akm_suites_avail:1; u64 vendor_scan_cookie; u64 remain_on_chan_cookie; diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index 7b360d209aba2..37eeb5e6686d3 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -403,10 +403,11 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, capa->flags |= WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD; if (ext_feature_isset(ext_features, len, - NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK) && - ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK)) + capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK; + if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X)) - capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X; if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_MFP_OPTIONAL)) @@ -428,6 +429,10 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION)) capa->flags |= WPA_DRIVER_FLAGS_OCE_STA; #endif /* CONFIG_MBO */ + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER)) + capa->flags |= WPA_DRIVER_FLAGS_FTM_RESPONDER; } @@ -782,6 +787,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) case QCA_NL80211_VENDOR_SUBCMD_ROAM: drv->roam_vendor_cmd_avail = 1; break; + case QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS: + drv->get_supported_akm_suites_avail = 1; + break; #endif /* CONFIG_DRIVER_NL80211_QCA */ } } @@ -954,6 +962,126 @@ static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv) } +static unsigned int get_akm_suites_info(struct nlattr *tb) +{ + int i, num; + unsigned int key_mgmt = 0; + u32 *akms; + + if (!tb) + return 0; + + num = nla_len(tb) / sizeof(u32); + akms = nla_data(tb); + for (i = 0; i < num; i++) { + u32 a = akms[i]; + + wpa_printf(MSG_DEBUG, + "nl80211: Supported AKM %02x-%02x-%02x:%u", + a >> 24, (a >> 16) & 0xff, + (a >> 8) & 0xff, a & 0xff); + switch (a) { + case RSN_AUTH_KEY_MGMT_UNSPEC_802_1X: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2; + break; + case RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + break; + case RSN_AUTH_KEY_MGMT_FT_802_1X: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT; + break; + case RSN_AUTH_KEY_MGMT_FT_PSK: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; + break; + case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B; + break; + case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192; + break; + case RSN_AUTH_KEY_MGMT_OWE: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OWE; + break; + case RSN_AUTH_KEY_MGMT_DPP: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_DPP; + break; + case RSN_AUTH_KEY_MGMT_FILS_SHA256: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256; + break; + case RSN_AUTH_KEY_MGMT_FILS_SHA384: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384; + break; + case RSN_AUTH_KEY_MGMT_FT_FILS_SHA256: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256; + break; + case RSN_AUTH_KEY_MGMT_FT_FILS_SHA384: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384; + break; + case RSN_AUTH_KEY_MGMT_SAE: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE; + break; + } + } + + return key_mgmt; +} + + +static int get_akm_suites_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + unsigned int *key_mgmt = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_VENDOR_DATA]) { + struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA]; + struct nlattr *tb_data[NL80211_ATTR_MAX + 1]; + + nla_parse(tb_data, NL80211_ATTR_MAX, + nla_data(nl_vend), nla_len(nl_vend), NULL); + + *key_mgmt = + get_akm_suites_info(tb_data[NL80211_ATTR_AKM_SUITES]); + } + + return NL_SKIP; +} + + +static int qca_nl80211_get_akm_suites(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + unsigned int key_mgmt = 0; + int ret; + + if (!drv->get_supported_akm_suites_avail) + return -1; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS)) { + nlmsg_free(msg); + return -1; + } + + ret = send_and_recv_msgs(drv, msg, get_akm_suites_handler, &key_mgmt); + if (!ret) { + wpa_printf(MSG_DEBUG, + "nl80211: Replace capa.key_mgmt based on driver advertised capabilities: 0x%x", + key_mgmt); + drv->capa.key_mgmt = key_mgmt; + } + + return ret; +} + + static int qca_nl80211_he_capab_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -1179,11 +1307,18 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 | WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 | WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 | - WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384; + WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 | + WPA_DRIVER_CAPA_KEY_MGMT_SAE; else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD) drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 | WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384; +#ifdef CONFIG_DRIVER_NL80211_QCA + /* Override drv->capa.key_mgmt based on driver advertised capability + * constraints, if available. */ + qca_nl80211_get_akm_suites(drv); +#endif /* CONFIG_DRIVER_NL80211_QCA */ + drv->capa.auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | WPA_DRIVER_AUTH_LEAP; @@ -1307,6 +1442,7 @@ static void phy_info_freq(struct hostapd_hw_modes *mode, u8 channel; chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); chan->flag = 0; + chan->allowed_bw = ~0; chan->dfs_cac_ms = 0; if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES) chan->chan = channel; @@ -1322,6 +1458,19 @@ static void phy_info_freq(struct hostapd_hw_modes *mode, if (tb_freq[NL80211_FREQUENCY_ATTR_GO_CONCURRENT]) chan->flag |= HOSTAPD_CHAN_GO_CONCURRENT; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_10MHZ]) + chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_10; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_20MHZ]) + chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_20; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS]) + chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_40P; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS]) + chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_40M; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_80MHZ]) + chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_80; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ]) + chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_160; + if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) { enum nl80211_dfs_state state = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]); @@ -1356,6 +1505,12 @@ static int phy_info_freqs(struct phy_info_arg *phy_info, [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, [NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 }, + [NL80211_FREQUENCY_ATTR_NO_10MHZ] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_20MHZ] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_HT40_PLUS] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_HT40_MINUS] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_80MHZ] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_160MHZ] = { .type = NLA_FLAG }, }; int new_channels = 0; struct hostapd_channel_data *channel; @@ -1932,6 +2087,61 @@ static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv, } +static const char * modestr(enum hostapd_hw_mode mode) +{ + switch (mode) { + case HOSTAPD_MODE_IEEE80211B: + return "802.11b"; + case HOSTAPD_MODE_IEEE80211G: + return "802.11g"; + case HOSTAPD_MODE_IEEE80211A: + return "802.11a"; + case HOSTAPD_MODE_IEEE80211AD: + return "802.11ad"; + default: + return "?"; + } +} + + +static void nl80211_dump_chan_list(struct hostapd_hw_modes *modes, + u16 num_modes) +{ + int i; + + if (!modes) + return; + + for (i = 0; i < num_modes; i++) { + struct hostapd_hw_modes *mode = &modes[i]; + char str[200]; + char *pos = str; + char *end = pos + sizeof(str); + int j, res; + + for (j = 0; j < mode->num_channels; j++) { + struct hostapd_channel_data *chan = &mode->channels[j]; + + res = os_snprintf(pos, end - pos, " %d%s%s%s", + chan->freq, + (chan->flag & HOSTAPD_CHAN_DISABLED) ? + "[DISABLED]" : "", + (chan->flag & HOSTAPD_CHAN_NO_IR) ? + "[NO_IR]" : "", + (chan->flag & HOSTAPD_CHAN_RADAR) ? + "[RADAR]" : ""); + if (os_snprintf_error(end - pos, res)) + break; + pos += res; + } + + *pos = '\0'; + wpa_printf(MSG_DEBUG, "nl80211: Mode IEEE %s:%s", + modestr(mode->mode), str); + } +} + + struct hostapd_hw_modes * nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags, u8 *dfs_domain) @@ -1963,6 +2173,8 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags, } if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) { + struct hostapd_hw_modes *modes; + nl80211_set_regulatory_flags(drv, &result); if (result.failed) { int i; @@ -1978,8 +2190,10 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags, *dfs_domain = result.dfs_domain; - return wpa_driver_nl80211_postprocess_modes(result.modes, - num_modes); + modes = wpa_driver_nl80211_postprocess_modes(result.modes, + num_modes); + nl80211_dump_chan_list(modes, *num_modes); + return modes; } return NULL; diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index 205b4cd4b0829..ee7b4da38d870 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -206,7 +206,8 @@ static void nl80211_parse_wmm_params(struct nlattr *wmm_attr, static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, - const u8 *frame, size_t len, struct nlattr *wmm) + const u8 *frame, size_t len, struct nlattr *wmm, + struct nlattr *req_ie) { const struct ieee80211_mgmt *mgmt; union wpa_event_data event; @@ -261,7 +262,10 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, len - 24 - sizeof(mgmt->u.assoc_resp); } - event.assoc_info.freq = drv->assoc_freq; + if (req_ie) { + event.assoc_info.req_ies = nla_data(req_ie); + event.assoc_info.req_ies_len = nla_len(req_ie); + } /* When this association was initiated outside of wpa_supplicant, * drv->ssid needs to be set here to satisfy later checking. */ @@ -273,6 +277,9 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, wpa_ssid_txt(drv->ssid, drv->ssid_len)); } + event.assoc_info.freq = drv->assoc_freq; + drv->first_bss->freq = drv->assoc_freq; + nl80211_parse_wmm_params(wmm, &event.assoc_info.wmm_params); wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); @@ -402,6 +409,7 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, } event.assoc_info.freq = nl80211_get_assoc_freq(drv); + drv->first_bss->freq = drv->assoc_freq; if ((!ssid || ssid[1] == 0 || ssid[1] > 32) && (ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid)) > 0) { @@ -868,7 +876,7 @@ static void mlme_event(struct i802_bss *bss, struct nlattr *addr, struct nlattr *timed_out, struct nlattr *freq, struct nlattr *ack, struct nlattr *cookie, struct nlattr *sig, - struct nlattr *wmm) + struct nlattr *wmm, struct nlattr *req_ie) { struct wpa_driver_nl80211_data *drv = bss->drv; const u8 *data; @@ -917,7 +925,8 @@ static void mlme_event(struct i802_bss *bss, mlme_event_auth(drv, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_ASSOCIATE: - mlme_event_assoc(drv, nla_data(frame), nla_len(frame), wmm); + mlme_event_assoc(drv, nla_data(frame), nla_len(frame), wmm, + req_ie); break; case NL80211_CMD_DEAUTHENTICATE: mlme_event_deauth_disassoc(drv, EVENT_DEAUTH, @@ -1429,16 +1438,23 @@ static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { union wpa_event_data data; + const u8 *addr; + u64 cookie = 0; - wpa_printf(MSG_DEBUG, "nl80211: Probe client event"); - - if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_ACK]) + addr = nla_data(tb[NL80211_ATTR_MAC]); + if (!addr) + return; + if (tb[NL80211_ATTR_COOKIE]) + cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); + wpa_printf(MSG_DEBUG, "nl80211: Probe client event (addr=" MACSTR + " ack=%d cookie=%llu)", MAC2STR(addr), + tb[NL80211_ATTR_ACK] != NULL, + (long long unsigned int) cookie); + if (!tb[NL80211_ATTR_ACK]) return; os_memset(&data, 0, sizeof(data)); - os_memcpy(data.client_poll.addr, - nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); - + os_memcpy(data.client_poll.addr, addr, ETH_ALEN); wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data); } @@ -2183,6 +2199,54 @@ static void nl80211_reg_change_event(struct wpa_driver_nl80211_data *drv, } +static void nl80211_dump_freq(const char *title, struct nlattr *nl_freq) +{ + static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, + [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, + }; + struct nlattr *tb[NL80211_FREQUENCY_ATTR_MAX + 1]; + u32 freq = 0, max_tx_power = 0; + + nla_parse(tb, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_freq), nla_len(nl_freq), freq_policy); + + if (tb[NL80211_FREQUENCY_ATTR_FREQ]) + freq = nla_get_u32(tb[NL80211_FREQUENCY_ATTR_FREQ]); + if (tb[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) + max_tx_power = + nla_get_u32(tb[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]); + + wpa_printf(MSG_DEBUG, + "nl80211: Channel (%s): freq=%u max_tx_power=%u%s%s%s", + title, freq, max_tx_power, + tb[NL80211_FREQUENCY_ATTR_DISABLED] ? " disabled" : "", + tb[NL80211_FREQUENCY_ATTR_NO_IR] ? " no-IR" : "", + tb[NL80211_FREQUENCY_ATTR_RADAR] ? " radar" : ""); +} + + +static void nl80211_reg_beacon_hint_event(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb[]) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint"); + os_memset(&data, 0, sizeof(data)); + data.channel_list_changed.initiator = REGDOM_BEACON_HINT; + + if (tb[NL80211_ATTR_FREQ_BEFORE]) + nl80211_dump_freq("before", tb[NL80211_ATTR_FREQ_BEFORE]); + if (tb[NL80211_ATTR_FREQ_AFTER]) + nl80211_dump_freq("after", tb[NL80211_ATTR_FREQ_AFTER]); + + wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data); +} + + static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { @@ -2214,11 +2278,9 @@ static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv, event.external_auth.ssid_len = nla_len(tb[NL80211_ATTR_SSID]); if (event.external_auth.ssid_len > SSID_MAX_LEN) return; - os_memcpy(event.external_auth.ssid, nla_data(tb[NL80211_ATTR_SSID]), - event.external_auth.ssid_len); + event.external_auth.ssid = nla_data(tb[NL80211_ATTR_SSID]); - os_memcpy(event.external_auth.bssid, nla_data(tb[NL80211_ATTR_BSSID]), - ETH_ALEN); + event.external_auth.bssid = nla_data(tb[NL80211_ATTR_BSSID]); wpa_printf(MSG_DEBUG, "nl80211: External auth action: %u, AKM: 0x%x", @@ -2327,7 +2389,6 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, struct nlattr **tb) { struct wpa_driver_nl80211_data *drv = bss->drv; - union wpa_event_data data; int external_scan_event = 0; wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s", @@ -2428,7 +2489,8 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], tb[NL80211_ATTR_COOKIE], tb[NL80211_ATTR_RX_SIGNAL_DBM], - tb[NL80211_ATTR_STA_WME]); + tb[NL80211_ATTR_STA_WME], + tb[NL80211_ATTR_REQ_IE]); break; case NL80211_CMD_CONNECT: case NL80211_CMD_ROAM: @@ -2480,11 +2542,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, nl80211_reg_change_event(drv, tb); break; case NL80211_CMD_REG_BEACON_HINT: - wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint"); - os_memset(&data, 0, sizeof(data)); - data.channel_list_changed.initiator = REGDOM_BEACON_HINT; - wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, - &data); + nl80211_reg_beacon_hint_event(drv, tb); break; case NL80211_CMD_NEW_STATION: nl80211_new_station_event(drv, bss, tb); @@ -2605,7 +2663,7 @@ int process_bss_event(struct nl_msg *msg, void *arg) tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], tb[NL80211_ATTR_COOKIE], tb[NL80211_ATTR_RX_SIGNAL_DBM], - tb[NL80211_ATTR_STA_WME]); + tb[NL80211_ATTR_STA_WME], NULL); break; case NL80211_CMD_UNEXPECTED_FRAME: nl80211_spurious_frame(bss, tb, 0); diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c index 33a8d359848fb..9afa5b304cf87 100644 --- a/src/drivers/driver_nl80211_scan.c +++ b/src/drivers/driver_nl80211_scan.c @@ -197,9 +197,9 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd, if (ssids == NULL) goto fail; for (i = 0; i < params->num_ssids; i++) { - wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", - params->ssids[i].ssid, - params->ssids[i].ssid_len); + wpa_printf(MSG_MSGDUMP, "nl80211: Scan SSID %s", + wpa_ssid_txt(params->ssids[i].ssid, + params->ssids[i].ssid_len)); if (nla_put(msg, i + 1, params->ssids[i].ssid_len, params->ssids[i].ssid)) goto fail; @@ -537,10 +537,10 @@ int wpa_driver_nl80211_sched_scan(void *priv, for (i = 0; i < drv->num_filter_ssids; i++) { struct nlattr *match_set_ssid; - wpa_hexdump_ascii(MSG_MSGDUMP, - "nl80211: Sched scan filter SSID", - drv->filter_ssids[i].ssid, - drv->filter_ssids[i].ssid_len); + wpa_printf(MSG_MSGDUMP, + "nl80211: Sched scan filter SSID %s", + wpa_ssid_txt(drv->filter_ssids[i].ssid, + drv->filter_ssids[i].ssid_len)); match_set_ssid = nla_nest_start(msg, i + 1); if (match_set_ssid == NULL || @@ -1098,9 +1098,9 @@ int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, if (ssids == NULL) goto fail; for (i = 0; i < params->num_ssids; i++) { - wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", - params->ssids[i].ssid, - params->ssids[i].ssid_len); + wpa_printf(MSG_MSGDUMP, "nl80211: Scan SSID %s", + wpa_ssid_txt(params->ssids[i].ssid, + params->ssids[i].ssid_len)); if (nla_put(msg, i + 1, params->ssids[i].ssid_len, params->ssids[i].ssid)) goto fail; diff --git a/src/drivers/driver_openbsd.c b/src/drivers/driver_openbsd.c index e94eda08f6210..c06e75c0f2841 100644 --- a/src/drivers/driver_openbsd.c +++ b/src/drivers/driver_openbsd.c @@ -62,7 +62,8 @@ static int wpa_driver_openbsd_get_capa(void *priv, struct wpa_driver_capa *capa) { os_memset(capa, 0, sizeof(*capa)); - capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK | + WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X; return 0; } diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c index e8a51354dd0cc..9beb6c46d367c 100644 --- a/src/drivers/driver_roboswitch.c +++ b/src/drivers/driver_roboswitch.c @@ -290,21 +290,26 @@ static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv, wpa_driver_roboswitch_addr_be16(addr, addr_be16); - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_CONF, - &_read, 1); + if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_CONF, &_read, 1) < 0) + return -1; /* If ARL control is disabled, there is nothing to leave. */ if (!(_read & (1 << 4))) return -1; - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_ADDR_1, addr_read, 3); - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, - &ports_read, 1); + if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, addr_read, 3) < 0 || + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, &ports_read, 1) < 0) + return -1; /* check if we occupy multiport address 1 */ if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) { - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_ADDR_2, addr_read, 3); - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_VEC_2, &ports_read, 1); + if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, addr_read, + 3) < 0 || + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &ports_read, + 1) < 0) + return -1; /* and multiport address 2 */ if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) { @@ -327,10 +332,13 @@ static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv, &ports_read, 1); } } else { - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_ADDR_2, addr_read, 3); - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_VEC_2, &ports_read, 1); + if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, addr_read, + 3) < 0 || + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &ports_read, + 1) < 0) + return -1; /* or multiport address 2 */ if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) { diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c index 20abaab4cd84c..f7755cccde27b 100644 --- a/src/drivers/driver_wext.c +++ b/src/drivers/driver_wext.c @@ -868,14 +868,16 @@ static int wext_hostap_ifname(struct wpa_driver_wext_data *drv, const char *ifname) { char buf[200], *res; - int type; + int type, ret; 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); + ret = snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net/%s/type", + drv->ifname, ifname); + if (os_snprintf_error(sizeof(buf), ret)) + return -1; f = fopen(buf, "r"); if (!f) @@ -1645,7 +1647,8 @@ static int wpa_driver_wext_get_range(void *priv) if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; if (range->enc_capa & IW_ENC_CAPA_4WAY_HANDSHAKE) - drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK | + WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X; drv->capa.auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | WPA_DRIVER_AUTH_LEAP; @@ -1676,7 +1679,7 @@ static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv, wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - if (!(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X)) return 0; if (!psk) diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak index 1496b47d0ad50..442c59cf4a5f6 100644 --- a/src/drivers/drivers.mak +++ b/src/drivers/drivers.mak @@ -22,6 +22,7 @@ ifdef CONFIG_DRIVER_MACSEC_LINUX DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX DRV_OBJS += ../src/drivers/driver_macsec_linux.o NEED_DRV_WIRED_COMMON=1 +NEED_LIBNL=y CONFIG_LIBNL3_ROUTE=y endif @@ -51,37 +52,7 @@ NEED_NETLINK=y NEED_LINUX_IOCTL=y NEED_RFKILL=y NEED_RADIOTAP=y - -ifdef CONFIG_LIBNL32 - DRV_LIBS += -lnl-3 - DRV_LIBS += -lnl-genl-3 - DRV_CFLAGS += -DCONFIG_LIBNL20 - ifdef LIBNL_INC - DRV_CFLAGS += -I$(LIBNL_INC) - else - PKG_CONFIG ?= pkg-config - DRV_CFLAGS += $(shell $(PKG_CONFIG) --cflags libnl-3.0) - endif -ifdef CONFIG_LIBNL3_ROUTE - DRV_LIBS += -lnl-route-3 - DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE -endif -else - ifdef CONFIG_LIBNL_TINY - DRV_LIBS += -lnl-tiny - else - ifndef CONFIG_OSX - DRV_LIBS += -lnl - endif - endif - - ifdef CONFIG_LIBNL20 - ifndef CONFIG_LIBNL_TINY - DRV_LIBS += -lnl-genl - endif - DRV_CFLAGS += -DCONFIG_LIBNL20 - endif -endif +NEED_LIBNL=y endif ifdef CONFIG_DRIVER_BSD @@ -183,26 +154,55 @@ endif ifdef CONFIG_VLAN_NETLINK ifdef CONFIG_FULL_DYNAMIC_VLAN +NEED_LIBNL=y +CONFIG_LIBNL3_ROUTE=y +endif +endif + +ifdef NEED_LIBNL +ifndef CONFIG_LIBNL32 +ifndef CONFIG_LIBNL20 +ifndef CONFIG_LIBNL_TINY +PKG_CONFIG ?= pkg-config +HAVE_LIBNL3 := $(shell $(PKG_CONFIG) --exists libnl-3.0; echo $$?) +ifeq ($(HAVE_LIBNL3),0) +CONFIG_LIBNL32=y +endif +endif +endif +endif + ifdef CONFIG_LIBNL32 DRV_LIBS += -lnl-3 DRV_LIBS += -lnl-genl-3 - DRV_LIBS += -lnl-route-3 DRV_CFLAGS += -DCONFIG_LIBNL20 + ifdef LIBNL_INC + DRV_CFLAGS += -I$(LIBNL_INC) + else + PKG_CONFIG ?= pkg-config + DRV_CFLAGS += $(shell $(PKG_CONFIG) --cflags libnl-3.0) + endif + ifdef CONFIG_LIBNL3_ROUTE + DRV_LIBS += -lnl-route-3 + DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE + endif else ifdef CONFIG_LIBNL_TINY DRV_LIBS += -lnl-tiny else - DRV_LIBS += -lnl + ifndef CONFIG_OSX + DRV_LIBS += -lnl + endif endif ifdef CONFIG_LIBNL20 - DRV_LIBS += -lnl-genl - DRV_LIBS += -lnl-route + ifndef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-genl + endif DRV_CFLAGS += -DCONFIG_LIBNL20 endif endif endif -endif ##### COMMON VARS DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS) diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk index cd25133af8088..599a0b5794fe7 100644 --- a/src/drivers/drivers.mk +++ b/src/drivers/drivers.mk @@ -23,6 +23,7 @@ DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX DRV_OBJS += src/drivers/driver_macsec_linux.c NEED_DRV_WIRED_COMMON=1 CONFIG_LIBNL3_ROUTE=y +NEED_LIBNL=y endif ifdef NEED_DRV_WIRED_COMMON @@ -46,29 +47,7 @@ NEED_NETLINK=y NEED_LINUX_IOCTL=y NEED_RFKILL=y NEED_RADIOTAP=y - -ifdef CONFIG_LIBNL32 - DRV_LIBS += -lnl-3 - DRV_LIBS += -lnl-genl-3 - DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3 -ifdef CONFIG_LIBNL3_ROUTE - DRV_LIBS += -lnl-route-3 - DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE -endif -else - ifdef CONFIG_LIBNL_TINY - DRV_LIBS += -lnl-tiny - else - DRV_LIBS += -lnl - endif - - ifdef CONFIG_LIBNL20 - ifndef CONFIG_LIBNL_TINY - DRV_LIBS += -lnl-genl - endif - DRV_CFLAGS += -DCONFIG_LIBNL20 - endif -endif +NEED_LIBNL=y endif ifdef CONFIG_DRIVER_BSD @@ -171,11 +150,20 @@ endif ifdef CONFIG_VLAN_NETLINK ifdef CONFIG_FULL_DYNAMIC_VLAN +NEED_LIBNL=y +CONFIG_LIBNL3_ROUTE=y +endif +endif + +ifdef NEED_LIBNL ifdef CONFIG_LIBNL32 DRV_LIBS += -lnl-3 DRV_LIBS += -lnl-genl-3 + DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3 +ifdef CONFIG_LIBNL3_ROUTE DRV_LIBS += -lnl-route-3 - DRV_CFLAGS += -DCONFIG_LIBNL20 + DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE +endif else ifdef CONFIG_LIBNL_TINY DRV_LIBS += -lnl-tiny @@ -184,13 +172,13 @@ else endif ifdef CONFIG_LIBNL20 - DRV_LIBS += -lnl-genl - DRV_LIBS += -lnl-route + ifndef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-genl + endif DRV_CFLAGS += -DCONFIG_LIBNL20 endif endif endif -endif ##### COMMON VARS DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS) diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c index e21147af1bcd6..7edb9df2edd49 100644 --- a/src/drivers/linux_ioctl.c +++ b/src/drivers/linux_ioctl.c @@ -12,6 +12,7 @@ #include <net/if_arp.h> #include "utils/common.h" +#include "common/linux_bridge.h" #include "linux_ioctl.h" @@ -119,25 +120,14 @@ int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr) } -#ifndef SIOCBRADDBR -#define SIOCBRADDBR 0x89a0 -#endif -#ifndef SIOCBRDELBR -#define SIOCBRDELBR 0x89a1 -#endif -#ifndef SIOCBRADDIF -#define SIOCBRADDIF 0x89a2 -#endif -#ifndef SIOCBRDELIF -#define SIOCBRDELIF 0x89a3 -#endif - - int linux_br_add(int sock, const char *brname) { if (ioctl(sock, SIOCBRADDBR, brname) < 0) { + int saved_errno = errno; + wpa_printf(MSG_DEBUG, "Could not add bridge %s: %s", brname, strerror(errno)); + errno = saved_errno; return -1; } @@ -170,8 +160,11 @@ int linux_br_add_if(int sock, const char *brname, const char *ifname) os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ); ifr.ifr_ifindex = ifindex; if (ioctl(sock, SIOCBRADDIF, &ifr) < 0) { + int saved_errno = errno; + wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge " "%s: %s", ifname, brname, strerror(errno)); + errno = saved_errno; return -1; } diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h index 1766a12b231c7..dd4f86ee286ec 100644 --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -1033,6 +1033,38 @@ * %NL80211_ATTR_CHANNEL_WIDTH,%NL80211_ATTR_NSS attributes with its * address(specified in %NL80211_ATTR_MAC). * + * @NL80211_CMD_GET_FTM_RESPONDER_STATS: Retrieve FTM responder statistics, in + * the %NL80211_ATTR_FTM_RESPONDER_STATS attribute. + * + * @NL80211_CMD_PEER_MEASUREMENT_START: start a (set of) peer measurement(s) + * with the given parameters, which are encapsulated in the nested + * %NL80211_ATTR_PEER_MEASUREMENTS attribute. Optionally, MAC address + * randomization may be enabled and configured by specifying the + * %NL80211_ATTR_MAC and %NL80211_ATTR_MAC_MASK attributes. + * If a timeout is requested, use the %NL80211_ATTR_TIMEOUT attribute. + * A u64 cookie for further %NL80211_ATTR_COOKIE use is is returned in + * the netlink extended ack message. + * + * To cancel a measurement, close the socket that requested it. + * + * Measurement results are reported to the socket that requested the + * measurement using @NL80211_CMD_PEER_MEASUREMENT_RESULT when they + * become available, so applications must ensure a large enough socket + * buffer size. + * + * Depending on driver support it may or may not be possible to start + * multiple concurrent measurements. + * @NL80211_CMD_PEER_MEASUREMENT_RESULT: This command number is used for the + * result notification from the driver to the requesting socket. + * @NL80211_CMD_PEER_MEASUREMENT_COMPLETE: Notification only, indicating that + * the measurement completed, using the measurement cookie + * (%NL80211_ATTR_COOKIE). + * + * @NL80211_CMD_NOTIFY_RADAR: Notify the kernel that a radar signal was + * detected and reported by a neighboring device on the channel + * indicated by %NL80211_ATTR_WIPHY_FREQ and other attributes + * determining the width and type. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1245,6 +1277,14 @@ enum nl80211_commands { NL80211_CMD_CONTROL_PORT_FRAME, + NL80211_CMD_GET_FTM_RESPONDER_STATS, + + NL80211_CMD_PEER_MEASUREMENT_START, + NL80211_CMD_PEER_MEASUREMENT_RESULT, + NL80211_CMD_PEER_MEASUREMENT_COMPLETE, + + NL80211_CMD_NOTIFY_RADAR, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1525,6 +1565,12 @@ enum nl80211_commands { * (a u32 with flags from &enum nl80211_wpa_versions). * @NL80211_ATTR_AKM_SUITES: Used with CONNECT, ASSOCIATE, and NEW_BEACON to * indicate which key management algorithm(s) to use (an array of u32). + * This attribute is also sent in response to @NL80211_CMD_GET_WIPHY, + * indicating the supported AKM suites, intended for specific drivers which + * implement SME and have constraints on which AKMs are supported and also + * the cases where an AKM support is offloaded to the driver/firmware. + * If there is no such notification from the driver, user space should + * assume the driver supports all the AKM suites. * * @NL80211_ATTR_REQ_IE: (Re)association request information elements as * sent out by the card, for ROAM and successful CONNECT events. @@ -1701,7 +1747,7 @@ enum nl80211_commands { * the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID * is included in the probe request, but the match attributes * will never let it go through), -EINVAL may be returned. - * If ommited, no filtering is done. + * If omitted, no filtering is done. * * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported * interface combinations. In each nested item, it contains attributes @@ -1806,7 +1852,7 @@ enum nl80211_commands { * * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be * used by the drivers which has MLME in firmware and does not have support - * to report per station tx/rx activity to free up the staion entry from + * to report per station tx/rx activity to free up the station entry from * the list. This needs to be used when the driver advertises the * capability to timeout the stations. * @@ -2167,7 +2213,7 @@ enum nl80211_commands { * * @NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST: When present the RSSI level for BSSs in * the specified band is to be adjusted before doing - * %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparision to figure out + * %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparison to figure out * better BSSs. The attribute value is a packed structure * value as specified by &struct nl80211_bss_select_rssi_adjust. * @@ -2220,10 +2266,10 @@ enum nl80211_commands { * &enum nl80211_external_auth_action value). This is used with the * %NL80211_CMD_EXTERNAL_AUTH request event. * @NL80211_ATTR_EXTERNAL_AUTH_SUPPORT: Flag attribute indicating that the user - * space supports external authentication. This attribute shall be used - * only with %NL80211_CMD_CONNECT request. The driver may offload - * authentication processing to user space if this capability is indicated - * in NL80211_CMD_CONNECT requests from the user space. + * space supports external authentication. This attribute shall be used + * with %NL80211_CMD_CONNECT and %NL80211_CMD_START_AP request. The driver + * may offload authentication processing to user space if this capability + * is indicated in the respective requests from the user space. * * @NL80211_ATTR_NSS: Station's New/updated RX_NSS value notified using this * u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED. @@ -2241,6 +2287,27 @@ enum nl80211_commands { * association request when used with NL80211_CMD_NEW_STATION). Can be set * only if %NL80211_STA_FLAG_WME is set. * + * @NL80211_ATTR_FTM_RESPONDER: nested attribute which user-space can include + * in %NL80211_CMD_START_AP or %NL80211_CMD_SET_BEACON for fine timing + * measurement (FTM) responder functionality and containing parameters as + * possible, see &enum nl80211_ftm_responder_attr + * + * @NL80211_ATTR_FTM_RESPONDER_STATS: Nested attribute with FTM responder + * statistics, see &enum nl80211_ftm_responder_stats. + * + * @NL80211_ATTR_TIMEOUT: Timeout for the given operation in milliseconds (u32), + * if the attribute is not given no timeout is requested. Note that 0 is an + * invalid value. + * + * @NL80211_ATTR_PEER_MEASUREMENTS: peer measurements request (and result) + * data, uses nested attributes specified in + * &enum nl80211_peer_measurement_attrs. + * This is also used for capability advertisement in the wiphy information, + * with the appropriate sub-attributes. + * + * @NL80211_ATTR_AIRTIME_WEIGHT: Station's weight when scheduled by the airtime + * scheduler. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2682,6 +2749,16 @@ enum nl80211_attrs { NL80211_ATTR_HE_CAPABILITY, + NL80211_ATTR_FTM_RESPONDER, + + NL80211_ATTR_FTM_RESPONDER_STATS, + + NL80211_ATTR_TIMEOUT, + + NL80211_ATTR_PEER_MEASUREMENTS, + + NL80211_ATTR_AIRTIME_WEIGHT, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3051,6 +3128,17 @@ enum nl80211_sta_bss_param { * @NL80211_STA_INFO_PAD: attribute used for padding for 64-bit alignment * @NL80211_STA_INFO_ACK_SIGNAL: signal strength of the last ACK frame(u8, dBm) * @NL80211_STA_INFO_ACK_SIGNAL_AVG: avg signal strength of ACK frames (s8, dBm) + * @NL80211_STA_INFO_RX_MPDUS: total number of received packets (MPDUs) + * (u32, from this station) + * @NL80211_STA_INFO_FCS_ERROR_COUNT: total number of packets (MPDUs) received + * with an FCS error (u32, from this station). This count may not include + * some packets with an FCS error due to TA corruption. Hence this counter + * might not be fully accurate. + * @NL80211_STA_INFO_CONNECTED_TO_GATE: set to true if STA has a path to a + * mesh gate (u8, 0 or 1) + * @NL80211_STA_INFO_TX_DURATION: aggregate PPDU duration for all frames + * sent to the station (u64, usec) + * @NL80211_STA_INFO_AIRTIME_WEIGHT: current airtime weight for station (u16) * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -3091,6 +3179,11 @@ enum nl80211_sta_info { NL80211_STA_INFO_PAD, NL80211_STA_INFO_ACK_SIGNAL, NL80211_STA_INFO_ACK_SIGNAL_AVG, + NL80211_STA_INFO_RX_MPDUS, + NL80211_STA_INFO_FCS_ERROR_COUNT, + NL80211_STA_INFO_CONNECTED_TO_GATE, + NL80211_STA_INFO_TX_DURATION, + NL80211_STA_INFO_AIRTIME_WEIGHT, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, @@ -3200,8 +3293,10 @@ enum nl80211_mpath_flags { * &enum nl80211_mpath_flags; * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries + * @NL80211_MPATH_INFO_HOP_COUNT: hop count to destination + * @NL80211_MPATH_INFO_PATH_CHANGE: total number of path changes to destination * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number - * currently defind + * currently defined * @__NL80211_MPATH_INFO_AFTER_LAST: internal use */ enum nl80211_mpath_info { @@ -3213,6 +3308,8 @@ enum nl80211_mpath_info { NL80211_MPATH_INFO_FLAGS, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, NL80211_MPATH_INFO_DISCOVERY_RETRIES, + NL80211_MPATH_INFO_HOP_COUNT, + NL80211_MPATH_INFO_PATH_CHANGE, /* keep last */ __NL80211_MPATH_INFO_AFTER_LAST, @@ -3870,6 +3967,11 @@ enum nl80211_mesh_power_mode { * 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_CONNECTED_TO_GATE: If set to true then this mesh STA + * will advertise that it is connected to a gate in the mesh formation + * field. If left unset then the mesh formation field will only + * advertise such if there is an active root mesh path. + * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use */ enum nl80211_meshconf_params { @@ -3902,6 +4004,7 @@ enum nl80211_meshconf_params { NL80211_MESHCONF_POWER_MODE, NL80211_MESHCONF_AWAKE_WINDOW, NL80211_MESHCONF_PLINK_TIMEOUT, + NL80211_MESHCONF_CONNECTED_TO_GATE, /* keep last */ __NL80211_MESHCONF_ATTR_AFTER_LAST, @@ -4834,7 +4937,7 @@ enum nl80211_iface_limit_attrs { * numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4 * => allows a STA plus three P2P interfaces * - * The list of these four possiblities could completely be contained + * The list of these four possibilities could completely be contained * within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate * that any of these groups must match. * @@ -4864,7 +4967,7 @@ enum nl80211_if_combination_attrs { * enum nl80211_plink_state - state of a mesh peer link finite state machine * * @NL80211_PLINK_LISTEN: initial state, considered the implicit - * state of non existant mesh peer links + * state of non existent mesh peer links * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to * this mesh peer * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received @@ -5225,6 +5328,20 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT: Driver/device can omit all data * except for supported rates from the probe request content if requested * by the %NL80211_SCAN_FLAG_MIN_PREQ_CONTENT flag. + * @NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER: Driver supports enabling fine + * timing measurement responder role. + * + * @NL80211_EXT_FEATURE_CAN_REPLACE_PTK0: Driver/device confirm that they are + * able to rekey an in-use key correctly. Userspace must not rekey PTK keys + * if this flag is not set. Ignoring this can leak clear text packets and/or + * freeze the connection. + * + * @NL80211_EXT_FEATURE_AIRTIME_FAIRNESS: Driver supports getting airtime + * fairness for transmitted packets and has enabled airtime fairness + * scheduling. + * + * @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching + * (set/del PMKSA operations) in AP mode. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -5263,6 +5380,10 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_TXQS, NL80211_EXT_FEATURE_SCAN_RANDOM_SN, NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT, + NL80211_EXT_FEATURE_CAN_REPLACE_PTK0, + NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER, + NL80211_EXT_FEATURE_AIRTIME_FAIRNESS, + NL80211_EXT_FEATURE_AP_PMKSA_CACHING, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, @@ -5347,7 +5468,7 @@ enum nl80211_timeout_reason { * request parameters IE in the probe request * @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at - * rate of at least 5.5M. In case non OCE AP is dicovered in the channel, + * rate of at least 5.5M. In case non OCE AP is discovered in the channel, * only the first probe req in the channel will be sent in high rate. * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request * tx deferral (dot11FILSProbeDelay shall be set to 15ms) @@ -5514,9 +5635,14 @@ enum nl80211_crit_proto_id { * Used by cfg80211_rx_mgmt() * * @NL80211_RXMGMT_FLAG_ANSWERED: frame was answered by device/driver. + * @NL80211_RXMGMT_FLAG_EXTERNAL_AUTH: Host driver intends to offload + * the authentication. Exclusively defined for host drivers that + * advertises the SME functionality but would like the userspace + * to handle certain authentication algorithms (e.g. SAE). */ enum nl80211_rxmgmt_flags { NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0, + NL80211_RXMGMT_FLAG_EXTERNAL_AUTH = 1 << 1, }; /* @@ -5802,4 +5928,458 @@ enum nl80211_external_auth_action { NL80211_EXTERNAL_AUTH_ABORT, }; +/** + * enum nl80211_ftm_responder_attributes - fine timing measurement + * responder attributes + * @__NL80211_FTM_RESP_ATTR_INVALID: Invalid + * @NL80211_FTM_RESP_ATTR_ENABLED: FTM responder is enabled + * @NL80211_FTM_RESP_ATTR_LCI: The content of Measurement Report Element + * (9.4.2.22 in 802.11-2016) with type 8 - LCI (9.4.2.22.10), + * i.e. starting with the measurement token + * @NL80211_FTM_RESP_ATTR_CIVIC: The content of Measurement Report Element + * (9.4.2.22 in 802.11-2016) with type 11 - Civic (Section 9.4.2.22.13), + * i.e. starting with the measurement token + * @__NL80211_FTM_RESP_ATTR_LAST: Internal + * @NL80211_FTM_RESP_ATTR_MAX: highest FTM responder attribute. + */ +enum nl80211_ftm_responder_attributes { + __NL80211_FTM_RESP_ATTR_INVALID, + + NL80211_FTM_RESP_ATTR_ENABLED, + NL80211_FTM_RESP_ATTR_LCI, + NL80211_FTM_RESP_ATTR_CIVICLOC, + + /* keep last */ + __NL80211_FTM_RESP_ATTR_LAST, + NL80211_FTM_RESP_ATTR_MAX = __NL80211_FTM_RESP_ATTR_LAST - 1, +}; + +/* + * enum nl80211_ftm_responder_stats - FTM responder statistics + * + * These attribute types are used with %NL80211_ATTR_FTM_RESPONDER_STATS + * when getting FTM responder statistics. + * + * @__NL80211_FTM_STATS_INVALID: attribute number 0 is reserved + * @NL80211_FTM_STATS_SUCCESS_NUM: number of FTM sessions in which all frames + * were ssfully answered (u32) + * @NL80211_FTM_STATS_PARTIAL_NUM: number of FTM sessions in which part of the + * frames were successfully answered (u32) + * @NL80211_FTM_STATS_FAILED_NUM: number of failed FTM sessions (u32) + * @NL80211_FTM_STATS_ASAP_NUM: number of ASAP sessions (u32) + * @NL80211_FTM_STATS_NON_ASAP_NUM: number of non-ASAP sessions (u32) + * @NL80211_FTM_STATS_TOTAL_DURATION_MSEC: total sessions durations - gives an + * indication of how much time the responder was busy (u64, msec) + * @NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM: number of unknown FTM triggers - + * triggers from initiators that didn't finish successfully the negotiation + * phase with the responder (u32) + * @NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM: number of FTM reschedule requests + * - initiator asks for a new scheduling although it already has scheduled + * FTM slot (u32) + * @NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM: number of FTM triggers out of + * scheduled window (u32) + * @NL80211_FTM_STATS_PAD: used for padding, ignore + * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal + * @NL80211_FTM_STATS_MAX: highest possible FTM responder stats attribute + */ +enum nl80211_ftm_responder_stats { + __NL80211_FTM_STATS_INVALID, + NL80211_FTM_STATS_SUCCESS_NUM, + NL80211_FTM_STATS_PARTIAL_NUM, + NL80211_FTM_STATS_FAILED_NUM, + NL80211_FTM_STATS_ASAP_NUM, + NL80211_FTM_STATS_NON_ASAP_NUM, + NL80211_FTM_STATS_TOTAL_DURATION_MSEC, + NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM, + NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM, + NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM, + NL80211_FTM_STATS_PAD, + + /* keep last */ + __NL80211_FTM_STATS_AFTER_LAST, + NL80211_FTM_STATS_MAX = __NL80211_FTM_STATS_AFTER_LAST - 1 +}; + +/** + * enum nl80211_preamble - frame preamble types + * @NL80211_PREAMBLE_LEGACY: legacy (HR/DSSS, OFDM, ERP PHY) preamble + * @NL80211_PREAMBLE_HT: HT preamble + * @NL80211_PREAMBLE_VHT: VHT preamble + * @NL80211_PREAMBLE_DMG: DMG preamble + */ +enum nl80211_preamble { + NL80211_PREAMBLE_LEGACY, + NL80211_PREAMBLE_HT, + NL80211_PREAMBLE_VHT, + NL80211_PREAMBLE_DMG, +}; + +/** + * enum nl80211_peer_measurement_type - peer measurement types + * @NL80211_PMSR_TYPE_INVALID: invalid/unused, needed as we use + * these numbers also for attributes + * + * @NL80211_PMSR_TYPE_FTM: flight time measurement + * + * @NUM_NL80211_PMSR_TYPES: internal + * @NL80211_PMSR_TYPE_MAX: highest type number + */ +enum nl80211_peer_measurement_type { + NL80211_PMSR_TYPE_INVALID, + + NL80211_PMSR_TYPE_FTM, + + NUM_NL80211_PMSR_TYPES, + NL80211_PMSR_TYPE_MAX = NUM_NL80211_PMSR_TYPES - 1 +}; + +/** + * enum nl80211_peer_measurement_status - peer measurement status + * @NL80211_PMSR_STATUS_SUCCESS: measurement completed successfully + * @NL80211_PMSR_STATUS_REFUSED: measurement was locally refused + * @NL80211_PMSR_STATUS_TIMEOUT: measurement timed out + * @NL80211_PMSR_STATUS_FAILURE: measurement failed, a type-dependent + * reason may be available in the response data + */ +enum nl80211_peer_measurement_status { + NL80211_PMSR_STATUS_SUCCESS, + NL80211_PMSR_STATUS_REFUSED, + NL80211_PMSR_STATUS_TIMEOUT, + NL80211_PMSR_STATUS_FAILURE, +}; + +/** + * enum nl80211_peer_measurement_req - peer measurement request attributes + * @__NL80211_PMSR_REQ_ATTR_INVALID: invalid + * + * @NL80211_PMSR_REQ_ATTR_DATA: This is a nested attribute with measurement + * type-specific request data inside. The attributes used are from the + * enums named nl80211_peer_measurement_<type>_req. + * @NL80211_PMSR_REQ_ATTR_GET_AP_TSF: include AP TSF timestamp, if supported + * (flag attribute) + * + * @NUM_NL80211_PMSR_REQ_ATTRS: internal + * @NL80211_PMSR_REQ_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_req { + __NL80211_PMSR_REQ_ATTR_INVALID, + + NL80211_PMSR_REQ_ATTR_DATA, + NL80211_PMSR_REQ_ATTR_GET_AP_TSF, + + /* keep last */ + NUM_NL80211_PMSR_REQ_ATTRS, + NL80211_PMSR_REQ_ATTR_MAX = NUM_NL80211_PMSR_REQ_ATTRS - 1 +}; + +/** + * enum nl80211_peer_measurement_resp - peer measurement response attributes + * @__NL80211_PMSR_RESP_ATTR_INVALID: invalid + * + * @NL80211_PMSR_RESP_ATTR_DATA: This is a nested attribute with measurement + * type-specific results inside. The attributes used are from the enums + * named nl80211_peer_measurement_<type>_resp. + * @NL80211_PMSR_RESP_ATTR_STATUS: u32 value with the measurement status + * (using values from &enum nl80211_peer_measurement_status.) + * @NL80211_PMSR_RESP_ATTR_HOST_TIME: host time (%CLOCK_BOOTTIME) when the + * result was measured; this value is not expected to be accurate to + * more than 20ms. (u64, nanoseconds) + * @NL80211_PMSR_RESP_ATTR_AP_TSF: TSF of the AP that the interface + * doing the measurement is connected to when the result was measured. + * This shall be accurately reported if supported and requested + * (u64, usec) + * @NL80211_PMSR_RESP_ATTR_FINAL: If results are sent to the host partially + * (*e.g. with FTM per-burst data) this flag will be cleared on all but + * the last result; if all results are combined it's set on the single + * result. + * @NL80211_PMSR_RESP_ATTR_PAD: padding for 64-bit attributes, ignore + * + * @NUM_NL80211_PMSR_RESP_ATTRS: internal + * @NL80211_PMSR_RESP_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_resp { + __NL80211_PMSR_RESP_ATTR_INVALID, + + NL80211_PMSR_RESP_ATTR_DATA, + NL80211_PMSR_RESP_ATTR_STATUS, + NL80211_PMSR_RESP_ATTR_HOST_TIME, + NL80211_PMSR_RESP_ATTR_AP_TSF, + NL80211_PMSR_RESP_ATTR_FINAL, + NL80211_PMSR_RESP_ATTR_PAD, + + /* keep last */ + NUM_NL80211_PMSR_RESP_ATTRS, + NL80211_PMSR_RESP_ATTR_MAX = NUM_NL80211_PMSR_RESP_ATTRS - 1 +}; + +/** + * enum nl80211_peer_measurement_peer_attrs - peer attributes for measurement + * @__NL80211_PMSR_PEER_ATTR_INVALID: invalid + * + * @NL80211_PMSR_PEER_ATTR_ADDR: peer's MAC address + * @NL80211_PMSR_PEER_ATTR_CHAN: channel definition, nested, using top-level + * attributes like %NL80211_ATTR_WIPHY_FREQ etc. + * @NL80211_PMSR_PEER_ATTR_REQ: This is a nested attribute indexed by + * measurement type, with attributes from the + * &enum nl80211_peer_measurement_req inside. + * @NL80211_PMSR_PEER_ATTR_RESP: This is a nested attribute indexed by + * measurement type, with attributes from the + * &enum nl80211_peer_measurement_resp inside. + * + * @NUM_NL80211_PMSR_PEER_ATTRS: internal + * @NL80211_PMSR_PEER_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_peer_attrs { + __NL80211_PMSR_PEER_ATTR_INVALID, + + NL80211_PMSR_PEER_ATTR_ADDR, + NL80211_PMSR_PEER_ATTR_CHAN, + NL80211_PMSR_PEER_ATTR_REQ, + NL80211_PMSR_PEER_ATTR_RESP, + + /* keep last */ + NUM_NL80211_PMSR_PEER_ATTRS, + NL80211_PMSR_PEER_ATTR_MAX = NUM_NL80211_PMSR_PEER_ATTRS - 1, +}; + +/** + * enum nl80211_peer_measurement_attrs - peer measurement attributes + * @__NL80211_PMSR_ATTR_INVALID: invalid + * + * @NL80211_PMSR_ATTR_MAX_PEERS: u32 attribute used for capability + * advertisement only, indicates the maximum number of peers + * measurements can be done with in a single request + * @NL80211_PMSR_ATTR_REPORT_AP_TSF: flag attribute in capability + * indicating that the connected AP's TSF can be reported in + * measurement results + * @NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR: flag attribute in capability + * indicating that MAC address randomization is supported. + * @NL80211_PMSR_ATTR_TYPE_CAPA: capabilities reported by the device, + * this contains a nesting indexed by measurement type, and + * type-specific capabilities inside, which are from the enums + * named nl80211_peer_measurement_<type>_capa. + * @NL80211_PMSR_ATTR_PEERS: nested attribute, the nesting index is + * meaningless, just a list of peers to measure with, with the + * sub-attributes taken from + * &enum nl80211_peer_measurement_peer_attrs. + * + * @NUM_NL80211_PMSR_ATTR: internal + * @NL80211_PMSR_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_attrs { + __NL80211_PMSR_ATTR_INVALID, + + NL80211_PMSR_ATTR_MAX_PEERS, + NL80211_PMSR_ATTR_REPORT_AP_TSF, + NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR, + NL80211_PMSR_ATTR_TYPE_CAPA, + NL80211_PMSR_ATTR_PEERS, + + /* keep last */ + NUM_NL80211_PMSR_ATTR, + NL80211_PMSR_ATTR_MAX = NUM_NL80211_PMSR_ATTR - 1 +}; + +/** + * enum nl80211_peer_measurement_ftm_capa - FTM capabilities + * @__NL80211_PMSR_FTM_CAPA_ATTR_INVALID: invalid + * + * @NL80211_PMSR_FTM_CAPA_ATTR_ASAP: flag attribute indicating ASAP mode + * is supported + * @NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP: flag attribute indicating non-ASAP + * mode is supported + * @NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI: flag attribute indicating if LCI + * data can be requested during the measurement + * @NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC: flag attribute indicating if civic + * location data can be requested during the measurement + * @NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES: u32 bitmap attribute of bits + * from &enum nl80211_preamble. + * @NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS: bitmap of values from + * &enum nl80211_chan_width indicating the supported channel + * bandwidths for FTM. Note that a higher channel bandwidth may be + * configured to allow for other measurements types with different + * bandwidth requirement in the same measurement. + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT: u32 attribute indicating + * the maximum bursts exponent that can be used (if not present anything + * is valid) + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST: u32 attribute indicating + * the maximum FTMs per burst (if not present anything is valid) + * + * @NUM_NL80211_PMSR_FTM_CAPA_ATTR: internal + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_ftm_capa { + __NL80211_PMSR_FTM_CAPA_ATTR_INVALID, + + NL80211_PMSR_FTM_CAPA_ATTR_ASAP, + NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP, + NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI, + NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC, + NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES, + NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS, + NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT, + NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST, + + /* keep last */ + NUM_NL80211_PMSR_FTM_CAPA_ATTR, + NL80211_PMSR_FTM_CAPA_ATTR_MAX = NUM_NL80211_PMSR_FTM_CAPA_ATTR - 1 +}; + +/** + * enum nl80211_peer_measurement_ftm_req - FTM request attributes + * @__NL80211_PMSR_FTM_REQ_ATTR_INVALID: invalid + * + * @NL80211_PMSR_FTM_REQ_ATTR_ASAP: ASAP mode requested (flag) + * @NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE: preamble type (see + * &enum nl80211_preamble), optional for DMG (u32) + * @NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP: number of bursts exponent as in + * 802.11-2016 9.4.2.168 "Fine Timing Measurement Parameters element" + * (u8, 0-15, optional with default 15 i.e. "no preference") + * @NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD: interval between bursts in units + * of 100ms (u16, optional with default 0) + * @NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION: burst duration, as in 802.11-2016 + * Table 9-257 "Burst Duration field encoding" (u8, 0-15, optional with + * default 15 i.e. "no preference") + * @NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST: number of successful FTM frames + * requested per burst + * (u8, 0-31, optional with default 0 i.e. "no preference") + * @NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES: number of FTMR frame retries + * (u8, default 3) + * @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI: request LCI data (flag) + * @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC: request civic location data + * (flag) + * + * @NUM_NL80211_PMSR_FTM_REQ_ATTR: internal + * @NL80211_PMSR_FTM_REQ_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_ftm_req { + __NL80211_PMSR_FTM_REQ_ATTR_INVALID, + + NL80211_PMSR_FTM_REQ_ATTR_ASAP, + NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE, + NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP, + NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD, + NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION, + NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST, + NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES, + NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI, + NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC, + + /* keep last */ + NUM_NL80211_PMSR_FTM_REQ_ATTR, + NL80211_PMSR_FTM_REQ_ATTR_MAX = NUM_NL80211_PMSR_FTM_REQ_ATTR - 1 +}; + +/** + * enum nl80211_peer_measurement_ftm_failure_reasons - FTM failure reasons + * @NL80211_PMSR_FTM_FAILURE_UNSPECIFIED: unspecified failure, not used + * @NL80211_PMSR_FTM_FAILURE_NO_RESPONSE: no response from the FTM responder + * @NL80211_PMSR_FTM_FAILURE_REJECTED: FTM responder rejected measurement + * @NL80211_PMSR_FTM_FAILURE_WRONG_CHANNEL: we already know the peer is + * on a different channel, so can't measure (if we didn't know, we'd + * try and get no response) + * @NL80211_PMSR_FTM_FAILURE_PEER_NOT_CAPABLE: peer can't actually do FTM + * @NL80211_PMSR_FTM_FAILURE_INVALID_TIMESTAMP: invalid T1/T4 timestamps + * received + * @NL80211_PMSR_FTM_FAILURE_PEER_BUSY: peer reports busy, you may retry + * later (see %NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME) + * @NL80211_PMSR_FTM_FAILURE_BAD_CHANGED_PARAMS: parameters were changed + * by the peer and are no longer supported + */ +enum nl80211_peer_measurement_ftm_failure_reasons { + NL80211_PMSR_FTM_FAILURE_UNSPECIFIED, + NL80211_PMSR_FTM_FAILURE_NO_RESPONSE, + NL80211_PMSR_FTM_FAILURE_REJECTED, + NL80211_PMSR_FTM_FAILURE_WRONG_CHANNEL, + NL80211_PMSR_FTM_FAILURE_PEER_NOT_CAPABLE, + NL80211_PMSR_FTM_FAILURE_INVALID_TIMESTAMP, + NL80211_PMSR_FTM_FAILURE_PEER_BUSY, + NL80211_PMSR_FTM_FAILURE_BAD_CHANGED_PARAMS, +}; + +/** + * enum nl80211_peer_measurement_ftm_resp - FTM response attributes + * @__NL80211_PMSR_FTM_RESP_ATTR_INVALID: invalid + * + * @NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON: FTM-specific failure reason + * (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX: optional, if bursts are reported + * as separate results then it will be the burst index 0...(N-1) and + * the top level will indicate partial results (u32) + * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS: number of FTM Request frames + * transmitted (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES: number of FTM Request frames + * that were acknowleged (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME: retry time received from the + * busy peer (u32, seconds) + * @NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP: actual number of bursts exponent + * used by the responder (similar to request, u8) + * @NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION: actual burst duration used by + * the responder (similar to request, u8) + * @NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST: actual FTMs per burst used + * by the responder (similar to request, u8) + * @NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG: average RSSI across all FTM action + * frames (optional, s32, 1/2 dBm) + * @NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD: RSSI spread across all FTM action + * frames (optional, s32, 1/2 dBm) + * @NL80211_PMSR_FTM_RESP_ATTR_TX_RATE: bitrate we used for the response to the + * FTM action frame (optional, nested, using &enum nl80211_rate_info + * attributes) + * @NL80211_PMSR_FTM_RESP_ATTR_RX_RATE: bitrate the responder used for the FTM + * action frame (optional, nested, using &enum nl80211_rate_info attrs) + * @NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG: average RTT (s64, picoseconds, optional + * but one of RTT/DIST must be present) + * @NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE: RTT variance (u64, ps^2, note that + * standard deviation is the square root of variance, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD: RTT spread (u64, picoseconds, + * optional) + * @NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG: average distance (s64, mm, optional + * but one of RTT/DIST must be present) + * @NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE: distance variance (u64, mm^2, note + * that standard deviation is the square root of variance, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD: distance spread (u64, mm, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_LCI: LCI data from peer (binary, optional); + * this is the contents of the Measurement Report Element (802.11-2016 + * 9.4.2.22.1) starting with the Measurement Token, with Measurement + * Type 8. + * @NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC: civic location data from peer + * (binary, optional); + * this is the contents of the Measurement Report Element (802.11-2016 + * 9.4.2.22.1) starting with the Measurement Token, with Measurement + * Type 11. + * @NL80211_PMSR_FTM_RESP_ATTR_PAD: ignore, for u64/s64 padding only + * + * @NUM_NL80211_PMSR_FTM_RESP_ATTR: internal + * @NL80211_PMSR_FTM_RESP_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_ftm_resp { + __NL80211_PMSR_FTM_RESP_ATTR_INVALID, + + NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON, + NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX, + NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS, + NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES, + NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME, + NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP, + NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION, + NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST, + NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG, + NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD, + NL80211_PMSR_FTM_RESP_ATTR_TX_RATE, + NL80211_PMSR_FTM_RESP_ATTR_RX_RATE, + NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG, + NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE, + NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD, + NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG, + NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE, + NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD, + NL80211_PMSR_FTM_RESP_ATTR_LCI, + NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC, + NL80211_PMSR_FTM_RESP_ATTR_PAD, + + /* keep last */ + NUM_NL80211_PMSR_FTM_RESP_ATTR, + NL80211_PMSR_FTM_RESP_ATTR_MAX = NUM_NL80211_PMSR_FTM_RESP_ATTR - 1 +}; + #endif /* __LINUX_NL80211_H */ diff --git a/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c index bfe8811e33f27..438baf190be71 100644 --- a/src/eap_common/eap_eke_common.c +++ b/src/eap_common/eap_eke_common.c @@ -399,7 +399,7 @@ int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key, /* SharedSecret = prf(0+, g ^ (x_s * x_p) (mod p)) */ len = dh->prime_len; if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len, - dhpriv, dh->prime_len, peer_pub, + NULL, 0, dhpriv, dh->prime_len, peer_pub, dh->prime_len, modexp, &len) < 0) return -1; if (len < dh->prime_len) { diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c index 88c6595887ab9..884150e6c1eb5 100644 --- a/src/eap_common/eap_pwd_common.c +++ b/src/eap_common/eap_pwd_common.c @@ -8,11 +8,15 @@ #include "includes.h" #include "common.h" +#include "utils/const_time.h" #include "crypto/sha256.h" #include "crypto/crypto.h" #include "eap_defs.h" #include "eap_pwd_common.h" +#define MAX_ECC_PRIME_LEN 66 + + /* The random function H(x) = HMAC-SHA256(0^32, x) */ struct crypto_hash * eap_pwd_h_init(void) { @@ -81,10 +85,23 @@ static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label, } +static int eap_pwd_suitable_group(u16 num) +{ + /* Do not allow ECC groups with prime under 256 bits based on guidance + * for the similar design in SAE. */ + return num == 19 || num == 20 || num == 21 || + num == 28 || num == 29 || num == 30; +} + + EAP_PWD_group * get_eap_pwd_group(u16 num) { EAP_PWD_group *grp; + if (!eap_pwd_suitable_group(num)) { + wpa_printf(MSG_INFO, "EAP-pwd: unsuitable group %u", num); + return NULL; + } grp = os_zalloc(sizeof(EAP_PWD_group)); if (!grp) return NULL; @@ -102,6 +119,15 @@ EAP_PWD_group * get_eap_pwd_group(u16 num) } +static void buf_shift_right(u8 *buf, size_t len, size_t bits) +{ + size_t i; + for (i = len - 1; i > 0; i--) + buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits); + buf[0] >>= bits; +} + + /* * compute a "random" secret point on an elliptic curve based * on the password and identities. @@ -113,33 +139,37 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, const u8 *token) { struct crypto_bignum *qr = NULL, *qnr = NULL, *one = NULL; + struct crypto_bignum *qr_or_qnr = NULL; + u8 qr_bin[MAX_ECC_PRIME_LEN]; + u8 qnr_bin[MAX_ECC_PRIME_LEN]; + u8 qr_or_qnr_bin[MAX_ECC_PRIME_LEN]; + u8 x_bin[MAX_ECC_PRIME_LEN]; struct crypto_bignum *tmp1 = NULL, *tmp2 = NULL, *pm1 = NULL; struct crypto_hash *hash; unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr; - int is_odd, ret = 0, check, found = 0; - size_t primebytelen, primebitlen; - struct crypto_bignum *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; + int ret = 0, check, res; + u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* + * mask */ + size_t primebytelen = 0, primebitlen; + struct crypto_bignum *x_candidate = NULL; const struct crypto_bignum *prime; + u8 mask, found_ctr = 0, is_odd = 0; if (grp->pwe) return -1; + os_memset(x_bin, 0, sizeof(x_bin)); + prime = crypto_ec_get_prime(grp->group); - cofactor = crypto_bignum_init(); grp->pwe = crypto_ec_point_init(grp->group); tmp1 = crypto_bignum_init(); pm1 = crypto_bignum_init(); one = crypto_bignum_init_set((const u8 *) "\x01", 1); - if (!cofactor || !grp->pwe || !tmp1 || !pm1 || !one) { + if (!grp->pwe || !tmp1 || !pm1 || !one) { wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums"); goto fail; } - if (crypto_ec_cofactor(grp->group, cofactor) < 0) { - wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for " - "curve"); - goto fail; - } primebitlen = crypto_ec_prime_len_bits(grp->group); primebytelen = crypto_ec_prime_len(grp->group); if ((prfbuf = os_malloc(primebytelen)) == NULL) { @@ -152,8 +182,6 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, /* get a random quadratic residue and nonresidue */ while (!qr || !qnr) { - int res; - if (crypto_bignum_rand(tmp1, prime) < 0) goto fail; res = crypto_bignum_legendre(tmp1, prime); @@ -167,6 +195,11 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, if (!tmp1) goto fail; } + if (crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), + primebytelen) < 0 || + crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), + primebytelen) < 0) + goto fail; os_memset(prfbuf, 0, primebytelen); ctr = 0; @@ -194,17 +227,16 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, eap_pwd_h_update(hash, &ctr, sizeof(ctr)); eap_pwd_h_final(hash, pwe_digest); - crypto_bignum_deinit(rnd, 1); - rnd = crypto_bignum_init_set(pwe_digest, SHA256_MAC_LEN); - if (!rnd) { - wpa_printf(MSG_INFO, "EAP-pwd: unable to create rnd"); - goto fail; - } + is_odd = const_time_select_u8( + found, is_odd, pwe_digest[SHA256_MAC_LEN - 1] & 0x01); if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN, (u8 *) "EAP-pwd Hunting And Pecking", os_strlen("EAP-pwd Hunting And Pecking"), prfbuf, primebitlen) < 0) goto fail; + if (primebitlen % 8) + buf_shift_right(prfbuf, primebytelen, + 8 - primebitlen % 8); crypto_bignum_deinit(x_candidate, 1); x_candidate = crypto_bignum_init_set(prfbuf, primebytelen); @@ -214,30 +246,20 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, goto fail; } - /* - * eap_pwd_kdf() returns a string of bits 0..primebitlen but - * BN_bin2bn will treat that string of bits as a big endian - * number. If the primebitlen is not an even multiple of 8 - * then excessive bits-- those _after_ primebitlen-- so now - * we have to shift right the amount we masked off. - */ - if ((primebitlen % 8) && - crypto_bignum_rshift(x_candidate, - (8 - (primebitlen % 8)), - x_candidate) < 0) - goto fail; - if (crypto_bignum_cmp(x_candidate, prime) >= 0) continue; - wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate", - prfbuf, primebytelen); + wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: x_candidate", + prfbuf, primebytelen); + const_time_select_bin(found, x_bin, prfbuf, primebytelen, + x_bin); /* * compute y^2 using the equation of the curve * * y^2 = x^3 + ax + b */ + crypto_bignum_deinit(tmp2, 1); tmp2 = crypto_ec_point_compute_y_sqr(grp->group, x_candidate); if (!tmp2) goto fail; @@ -260,13 +282,15 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, * Flip a coin, multiply by the random quadratic residue or the * random quadratic nonresidue and record heads or tails. */ - if (crypto_bignum_is_odd(tmp1)) { - crypto_bignum_mulmod(tmp2, qr, prime, tmp2); - check = 1; - } else { - crypto_bignum_mulmod(tmp2, qnr, prime, tmp2); - check = -1; - } + mask = const_time_eq_u8(crypto_bignum_is_odd(tmp1), 1); + check = const_time_select_s8(mask, 1, -1); + const_time_select_bin(mask, qr_bin, qnr_bin, primebytelen, + qr_or_qnr_bin); + crypto_bignum_deinit(qr_or_qnr, 1); + qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, primebytelen); + if (!qr_or_qnr || + crypto_bignum_mulmod(tmp2, qr_or_qnr, prime, tmp2) < 0) + goto fail; /* * Now it's safe to do legendre, if check is 1 then it's @@ -274,59 +298,12 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, * change result), if check is -1 then it's the opposite test * (multiplying a qr by qnr would make a qnr). */ - if (crypto_bignum_legendre(tmp2, prime) == check) { - if (found == 1) - continue; - - /* need to unambiguously identify the solution */ - is_odd = crypto_bignum_is_odd(rnd); - - /* - * We know x_candidate is a quadratic residue so set - * it here. - */ - if (crypto_ec_point_solve_y_coord(grp->group, grp->pwe, - x_candidate, - is_odd) != 0) { - wpa_printf(MSG_INFO, - "EAP-pwd: Could not solve for y"); - continue; - } - - /* - * If there's a solution to the equation then the point - * must be on the curve so why check again explicitly? - * OpenSSL code says this is required by X9.62. We're - * not X9.62 but it can't hurt just to be sure. - */ - if (!crypto_ec_point_is_on_curve(grp->group, - grp->pwe)) { - wpa_printf(MSG_INFO, - "EAP-pwd: point is not on curve"); - continue; - } - - if (!crypto_bignum_is_one(cofactor)) { - /* make sure the point is not in a small - * sub-group */ - if (crypto_ec_point_mul(grp->group, grp->pwe, - cofactor, - grp->pwe) != 0) { - wpa_printf(MSG_INFO, - "EAP-pwd: cannot multiply generator by order"); - continue; - } - if (crypto_ec_point_is_at_infinity(grp->group, - grp->pwe)) { - wpa_printf(MSG_INFO, - "EAP-pwd: point is at infinity"); - continue; - } - } - wpa_printf(MSG_DEBUG, - "EAP-pwd: found a PWE in %d tries", ctr); - found = 1; - } + res = crypto_bignum_legendre(tmp2, prime); + if (res == -2) + goto fail; + mask = const_time_eq(res, check); + found_ctr = const_time_select_u8(found, found_ctr, ctr); + found |= mask; } if (found == 0) { wpa_printf(MSG_INFO, @@ -334,6 +311,31 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, num); goto fail; } + + /* + * We know x_candidate is a quadratic residue so set it here. + */ + crypto_bignum_deinit(x_candidate, 1); + x_candidate = crypto_bignum_init_set(x_bin, primebytelen); + if (!x_candidate || + crypto_ec_point_solve_y_coord(grp->group, grp->pwe, x_candidate, + is_odd) != 0) { + wpa_printf(MSG_INFO, "EAP-pwd: Could not solve for y"); + goto fail; + } + + /* + * If there's a solution to the equation then the point must be on the + * curve so why check again explicitly? OpenSSL code says this is + * required by X9.62. We're not X9.62 but it can't hurt just to be sure. + */ + if (!crypto_ec_point_is_on_curve(grp->group, grp->pwe)) { + wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve"); + goto fail; + } + + wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %02d tries", found_ctr); + if (0) { fail: crypto_ec_point_deinit(grp->pwe, 1); @@ -341,16 +343,19 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, ret = 1; } /* cleanliness and order.... */ - crypto_bignum_deinit(cofactor, 1); crypto_bignum_deinit(x_candidate, 1); - crypto_bignum_deinit(rnd, 1); crypto_bignum_deinit(pm1, 0); crypto_bignum_deinit(tmp1, 1); crypto_bignum_deinit(tmp2, 1); crypto_bignum_deinit(qr, 1); crypto_bignum_deinit(qnr, 1); + crypto_bignum_deinit(qr_or_qnr, 1); crypto_bignum_deinit(one, 0); - os_free(prfbuf); + bin_clear_free(prfbuf, primebytelen); + os_memset(qr_bin, 0, sizeof(qr_bin)); + os_memset(qnr_bin, 0, sizeof(qnr_bin)); + os_memset(qr_or_qnr_bin, 0, sizeof(qr_or_qnr_bin)); + os_memset(pwe_digest, 0, sizeof(pwe_digest)); return ret; } @@ -416,3 +421,108 @@ int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k, return 1; } + + +static int eap_pwd_element_coord_ok(const struct crypto_bignum *prime, + const u8 *buf, size_t len) +{ + struct crypto_bignum *val; + int ok = 1; + + val = crypto_bignum_init_set(buf, len); + if (!val || crypto_bignum_is_zero(val) || + crypto_bignum_cmp(val, prime) >= 0) + ok = 0; + crypto_bignum_deinit(val, 0); + return ok; +} + + +struct crypto_ec_point * eap_pwd_get_element(EAP_PWD_group *group, + const u8 *buf) +{ + struct crypto_ec_point *element; + const struct crypto_bignum *prime; + size_t prime_len; + + prime = crypto_ec_get_prime(group->group); + prime_len = crypto_ec_prime_len(group->group); + + /* RFC 5931, 2.8.5.2.2: 0 < x,y < p */ + if (!eap_pwd_element_coord_ok(prime, buf, prime_len) || + !eap_pwd_element_coord_ok(prime, buf + prime_len, prime_len)) { + wpa_printf(MSG_INFO, "EAP-pwd: Invalid coordinate in element"); + return NULL; + } + + element = crypto_ec_point_from_bin(group->group, buf); + if (!element) { + wpa_printf(MSG_INFO, "EAP-pwd: EC point from element failed"); + return NULL; + } + + /* RFC 5931, 2.8.5.2.2: on curve and not the point at infinity */ + if (!crypto_ec_point_is_on_curve(group->group, element) || + crypto_ec_point_is_at_infinity(group->group, element)) { + wpa_printf(MSG_INFO, "EAP-pwd: Invalid element"); + goto fail; + } + +out: + return element; +fail: + crypto_ec_point_deinit(element, 0); + element = NULL; + goto out; +} + + +struct crypto_bignum * eap_pwd_get_scalar(EAP_PWD_group *group, const u8 *buf) +{ + struct crypto_bignum *scalar; + const struct crypto_bignum *order; + size_t order_len; + + order = crypto_ec_get_order(group->group); + order_len = crypto_ec_order_len(group->group); + + /* RFC 5931, 2.8.5.2: 1 < scalar < r */ + scalar = crypto_bignum_init_set(buf, order_len); + if (!scalar || crypto_bignum_is_zero(scalar) || + crypto_bignum_is_one(scalar) || + crypto_bignum_cmp(scalar, order) >= 0) { + wpa_printf(MSG_INFO, "EAP-pwd: received scalar is invalid"); + crypto_bignum_deinit(scalar, 0); + scalar = NULL; + } + + return scalar; +} + + +int eap_pwd_get_rand_mask(EAP_PWD_group *group, struct crypto_bignum *_rand, + struct crypto_bignum *_mask, + struct crypto_bignum *scalar) +{ + const struct crypto_bignum *order; + int count; + + order = crypto_ec_get_order(group->group); + + /* Select two random values rand,mask such that 1 < rand,mask < r and + * rand + mask mod r > 1. */ + for (count = 0; count < 100; count++) { + if (crypto_bignum_rand(_rand, order) == 0 && + !crypto_bignum_is_zero(_rand) && + crypto_bignum_rand(_mask, order) == 0 && + !crypto_bignum_is_zero(_mask) && + crypto_bignum_add(_rand, _mask, scalar) == 0 && + crypto_bignum_mod(scalar, order, scalar) == 0 && + !crypto_bignum_is_zero(scalar) && + !crypto_bignum_is_one(scalar)) + return 0; + } + + wpa_printf(MSG_INFO, "EAP-pwd: unable to get randomness"); + return -1; +} diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h index 6b07cf8f797c8..c48acee204d3b 100644 --- a/src/eap_common/eap_pwd_common.h +++ b/src/eap_common/eap_pwd_common.h @@ -67,5 +67,11 @@ int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k, 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); +struct crypto_ec_point * eap_pwd_get_element(EAP_PWD_group *group, + const u8 *buf); +struct crypto_bignum * eap_pwd_get_scalar(EAP_PWD_group *group, const u8 *buf); +int eap_pwd_get_rand_mask(EAP_PWD_group *group, struct crypto_bignum *_rand, + struct crypto_bignum *_mask, + struct crypto_bignum *scalar); #endif /* EAP_PWD_COMMON_H */ diff --git a/src/eap_common/eap_sake_common.c b/src/eap_common/eap_sake_common.c index 8819541b2264c..8ee9e32e1e488 100644 --- a/src/eap_common/eap_sake_common.c +++ b/src/eap_common/eap_sake_common.c @@ -1,6 +1,6 @@ /* * EAP server/peer: EAP-SAKE shared routines - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -201,14 +201,15 @@ int eap_sake_parse_attributes(const u8 *buf, size_t len, * @data2_len: Length of the data2 * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bytes of key to generate + * Returns: 0 on success or -1 on failure * * This function is used to derive new, cryptographically separate keys from a * given key (e.g., SMS). This is identical to the PRF used in IEEE 802.11i. */ -static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, - const u8 *data2, size_t data2_len, - u8 *buf, size_t buf_len) +static int eap_sake_kdf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, + const u8 *data2, size_t data2_len, + u8 *buf, size_t buf_len) { u8 counter = 0; size_t pos, plen; @@ -230,17 +231,21 @@ static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label, while (pos < buf_len) { plen = buf_len - pos; if (plen >= SHA1_MAC_LEN) { - hmac_sha1_vector(key, key_len, 4, addr, len, - &buf[pos]); + if (hmac_sha1_vector(key, key_len, 4, addr, len, + &buf[pos]) < 0) + return -1; pos += SHA1_MAC_LEN; } else { - hmac_sha1_vector(key, key_len, 4, addr, len, - hash); + if (hmac_sha1_vector(key, key_len, 4, addr, len, + hash) < 0) + return -1; os_memcpy(&buf[pos], hash, plen); break; } counter++; } + + return 0; } @@ -253,12 +258,13 @@ static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label, * @tek: Buffer for Temporary EAK Keys (TEK-Auth[16] | TEK-Cipher[16]) * @msk: Buffer for 64-byte MSK * @emsk: Buffer for 64-byte EMSK + * Returns: 0 on success or -1 on failure * * This function derives EAP-SAKE keys as defined in RFC 4763, section 3.2.6. */ -void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, - const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk, - u8 *emsk) +int eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, + const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk, + u8 *emsk) { u8 sms_a[EAP_SAKE_SMS_LEN]; u8 sms_b[EAP_SAKE_SMS_LEN]; @@ -268,14 +274,16 @@ void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-A", root_secret_a, EAP_SAKE_ROOT_SECRET_LEN); - eap_sake_kdf(root_secret_a, EAP_SAKE_ROOT_SECRET_LEN, - "SAKE Master Secret A", - rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN, - sms_a, EAP_SAKE_SMS_LEN); + if (eap_sake_kdf(root_secret_a, EAP_SAKE_ROOT_SECRET_LEN, + "SAKE Master Secret A", + rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN, + sms_a, EAP_SAKE_SMS_LEN) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-A", sms_a, EAP_SAKE_SMS_LEN); - eap_sake_kdf(sms_a, EAP_SAKE_SMS_LEN, "Transient EAP Key", - rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN, - tek, EAP_SAKE_TEK_LEN); + if (eap_sake_kdf(sms_a, EAP_SAKE_SMS_LEN, "Transient EAP Key", + rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN, + tek, EAP_SAKE_TEK_LEN) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Auth", tek, EAP_SAKE_TEK_AUTH_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Cipher", @@ -283,18 +291,21 @@ void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-B", root_secret_b, EAP_SAKE_ROOT_SECRET_LEN); - eap_sake_kdf(root_secret_b, EAP_SAKE_ROOT_SECRET_LEN, - "SAKE Master Secret B", - rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN, - sms_b, EAP_SAKE_SMS_LEN); + if (eap_sake_kdf(root_secret_b, EAP_SAKE_ROOT_SECRET_LEN, + "SAKE Master Secret B", + rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN, + sms_b, EAP_SAKE_SMS_LEN) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-B", sms_b, EAP_SAKE_SMS_LEN); - eap_sake_kdf(sms_b, EAP_SAKE_SMS_LEN, "Master Session Key", - rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN, - key_buf, sizeof(key_buf)); + if (eap_sake_kdf(sms_b, EAP_SAKE_SMS_LEN, "Master Session Key", + rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN, + key_buf, sizeof(key_buf)) < 0) + return -1; os_memcpy(msk, key_buf, EAP_MSK_LEN); os_memcpy(emsk, key_buf + EAP_MSK_LEN, EAP_EMSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: MSK", msk, EAP_MSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: EMSK", emsk, EAP_EMSK_LEN); + return 0; } @@ -312,6 +323,7 @@ void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, * @eap_len: EAP packet length * @mic_pos: MIC position in the EAP packet (must be [eap .. eap + eap_len]) * @mic: Buffer for the computed 16-byte MIC + * Returns: 0 on success or -1 on failure */ int eap_sake_compute_mic(const u8 *tek_auth, const u8 *rand_s, const u8 *rand_p, @@ -323,6 +335,7 @@ int eap_sake_compute_mic(const u8 *tek_auth, u8 _rand[2 * EAP_SAKE_RAND_LEN]; u8 *tmp, *pos; size_t tmplen; + int ret; tmplen = serverid_len + 1 + peerid_len + 1 + eap_len; tmp = os_malloc(tmplen); @@ -364,14 +377,14 @@ int eap_sake_compute_mic(const u8 *tek_auth, os_memcpy(pos, eap, eap_len); os_memset(pos + (mic_pos - eap), 0, EAP_SAKE_MIC_LEN); - eap_sake_kdf(tek_auth, EAP_SAKE_TEK_AUTH_LEN, - peer ? "Peer MIC" : "Server MIC", - _rand, 2 * EAP_SAKE_RAND_LEN, tmp, tmplen, - mic, EAP_SAKE_MIC_LEN); + ret = eap_sake_kdf(tek_auth, EAP_SAKE_TEK_AUTH_LEN, + peer ? "Peer MIC" : "Server MIC", + _rand, 2 * EAP_SAKE_RAND_LEN, tmp, tmplen, + mic, EAP_SAKE_MIC_LEN); os_free(tmp); - return 0; + return ret; } diff --git a/src/eap_common/eap_sake_common.h b/src/eap_common/eap_sake_common.h index 9e1e75745a115..a817a35d438cc 100644 --- a/src/eap_common/eap_sake_common.h +++ b/src/eap_common/eap_sake_common.h @@ -1,6 +1,6 @@ /* * EAP server/peer: EAP-SAKE shared routines - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -81,9 +81,9 @@ struct eap_sake_parse_attr { int eap_sake_parse_attributes(const u8 *buf, size_t len, struct eap_sake_parse_attr *attr); -void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, - const u8 *rand_s, const u8 *rand_p, - u8 *tek, u8 *msk, u8 *emsk); +int eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, + const u8 *rand_s, const u8 *rand_p, + u8 *tek, u8 *msk, u8 *emsk); int eap_sake_compute_mic(const u8 *tek_auth, const u8 *rand_s, const u8 *rand_p, const u8 *serverid, size_t serverid_len, diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h index d416afd56d59f..3a88f2abd236b 100644 --- a/src/eap_peer/eap_config.h +++ b/src/eap_peer/eap_config.h @@ -101,7 +101,7 @@ struct eap_peer_config { * certificate store (My user account) is used, whereas computer store * (Computer account) is used when running wpasvc as a service. */ - u8 *ca_cert; + char *ca_cert; /** * ca_path - Directory path for CA certificate files (PEM) @@ -112,7 +112,7 @@ struct eap_peer_config { * these certificates are added to the list of trusted CAs. ca_cert * may also be included in that case, but it is not required. */ - u8 *ca_path; + char *ca_path; /** * client_cert - File path to client certificate file (PEM/DER) @@ -126,7 +126,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *client_cert; + char *client_cert; /** * private_key - File path to client private key file (PEM/DER/PFX) @@ -153,7 +153,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *private_key; + char *private_key; /** * private_key_passwd - Password for private key file @@ -178,7 +178,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *dh_file; + char *dh_file; /** * subject_match - Constraint for server certificate subject @@ -194,7 +194,49 @@ struct eap_peer_config { * to do a suffix match against a possible domain name in the CN entry. * For such a use case, domain_suffix_match should be used instead. */ - u8 *subject_match; + char *subject_match; + + /** + * check_cert_subject - Constraint for server certificate subject fields + * + * If check_cert_subject is set, the value of every field will be + * checked against the DN of the subject in the authentication server + * certificate. If the values do not match, the certificate verification + * will fail, rejecting the server. This option allows wpa_supplicant to + * match every individual field in the right order against the DN of the + * subject in the server certificate. + * + * For example, check_cert_subject=C=US/O=XX/OU=ABC/OU=XYZ/CN=1234 will + * check every individual DN field of the subject in the server + * certificate. If OU=XYZ comes first in terms of the order in the + * server certificate (DN field of server certificate + * C=US/O=XX/OU=XYZ/OU=ABC/CN=1234), wpa_supplicant will reject the + * server because the order of 'OU' is not matching the specified string + * in check_cert_subject. + * + * This option also allows '*' as a wildcard. This option has some + * limitation. + * It can only be used as per the following example. + * + * For example, check_cert_subject=C=US/O=XX/OU=Production* and we have + * two servers and DN of the subject in the first server certificate is + * (C=US/O=XX/OU=Production Unit) and DN of the subject in the second + * server is (C=US/O=XX/OU=Production Factory). In this case, + * wpa_supplicant will allow both servers because the value of 'OU' + * field in both server certificates matches 'OU' value in + * 'check_cert_subject' up to 'wildcard'. + * + * (Allow all servers, e.g., check_cert_subject=*) + */ + char *check_cert_subject; + + /** + * check_cert_subject2 - Constraint for server certificate subject fields + * + * This field is like check_cert_subject, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + char *check_cert_subject2; /** * altsubject_match - Constraint for server certificate alt. subject @@ -212,23 +254,26 @@ struct eap_peer_config { * * Following types are supported: EMAIL, DNS, URI */ - u8 *altsubject_match; + char *altsubject_match; /** * domain_suffix_match - Constraint for server domain name * - * If set, this FQDN is used as a suffix match requirement for the - * server certificate in SubjectAltName dNSName element(s). If a - * matching dNSName is found, this constraint is met. If no dNSName - * values are present, this constraint is matched against SubjectName CN - * using same suffix match comparison. Suffix match here means that the - * host/domain name is compared one label at a time starting from the - * top-level domain and all the labels in domain_suffix_match shall be - * included in the certificate. The certificate may include additional - * sub-level labels in addition to the required labels. + * If set, this semicolon deliminated list of FQDNs is used as suffix + * match requirements for the server certificate in SubjectAltName + * dNSName element(s). If a matching dNSName is found against any of the + * specified values, this constraint is met. If no dNSName values are + * present, this constraint is matched against SubjectName CN using same + * suffix match comparison. Suffix match here means that the host/domain + * name is compared case-insentively one label at a time starting from + * the top-level domain and all the labels in domain_suffix_match shall + * be included in the certificate. The certificate may include + * additional sub-level labels in addition to the required labels. * * For example, domain_suffix_match=example.com would match - * test.example.com but would not match test-example.com. + * test.example.com but would not match test-example.com. Multiple + * match options can be specified in following manner: + * example.org;example.com. */ char *domain_suffix_match; @@ -244,6 +289,12 @@ struct eap_peer_config { * no subdomains or wildcard matches are allowed. Case-insensitive * comparison is used, so "Example.com" matches "example.com", but would * not match "test.Example.com". + * + * More than one match string can be provided by using semicolons to + * separate the strings (e.g., example.org;example.com). When multiple + * strings are specified, a match with any one of the values is + * considered a sufficient match for the certificate, i.e., the + * conditions are ORed together. */ char *domain_match; @@ -263,7 +314,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *ca_cert2; + char *ca_cert2; /** * ca_path2 - Directory path for CA certificate files (PEM) (Phase 2) @@ -277,7 +328,7 @@ struct eap_peer_config { * This field is like ca_path, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. */ - u8 *ca_path2; + char *ca_path2; /** * client_cert2 - File path to client certificate file @@ -290,7 +341,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *client_cert2; + char *client_cert2; /** * private_key2 - File path to client private key file @@ -303,7 +354,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *private_key2; + char *private_key2; /** * private_key2_passwd - Password for private key file @@ -324,7 +375,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *dh_file2; + char *dh_file2; /** * subject_match2 - Constraint for server certificate subject @@ -332,7 +383,7 @@ struct eap_peer_config { * This field is like subject_match, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. */ - u8 *subject_match2; + char *subject_match2; /** * altsubject_match2 - Constraint for server certificate alt. subject @@ -340,7 +391,7 @@ struct eap_peer_config { * This field is like altsubject_match, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. */ - u8 *altsubject_match2; + char *altsubject_match2; /** * domain_suffix_match2 - Constraint for server domain name diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c index 74cec7dd583f8..94ce57d6219f2 100644 --- a/src/eap_peer/eap_fast.c +++ b/src/eap_peer/eap_fast.c @@ -250,8 +250,8 @@ static void eap_fast_deinit(struct eap_sm *sm, void *priv) os_memset(data->key_data, 0, EAP_FAST_KEY_LEN); os_memset(data->emsk, 0, EAP_EMSK_LEN); os_free(data->session_id); - wpabuf_free(data->pending_phase2_req); - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_resp); os_free(data); } @@ -486,7 +486,7 @@ static int eap_fast_phase2_request(struct eap_sm *sm, (config->pending_req_identity || config->pending_req_password || config->pending_req_otp || config->pending_req_new_password || config->pending_req_sim)) { - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); } else if (*resp == NULL) return -1; @@ -801,7 +801,7 @@ static struct wpabuf * eap_fast_process_crypto_binding( ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; data->phase2_success = 0; - wpabuf_free(resp); + wpabuf_clear_free(resp); return NULL; } @@ -815,7 +815,7 @@ static struct wpabuf * eap_fast_process_crypto_binding( } else { wpa_printf(MSG_ERROR, "EAP-FAST: Failed to derive " "Session-Id"); - wpabuf_free(resp); + wpabuf_clear_free(resp); return NULL; } } @@ -1150,7 +1150,7 @@ static int eap_fast_encrypt_response(struct eap_sm *sm, wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt a Phase 2 " "frame"); } - wpabuf_free(resp); + wpabuf_clear_free(resp); return 0; } @@ -1328,14 +1328,14 @@ continue_req: wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 " "TLV frame (len=%lu)", (unsigned long) wpabuf_len(in_decrypted)); - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return -1; } res = eap_fast_process_decrypted(sm, data, ret, identifier, in_decrypted, out_data); - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return res; } @@ -1613,7 +1613,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, if (sm->waiting_ext_cert_check) { wpa_printf(MSG_DEBUG, "EAP-FAST: Waiting external server certificate validation"); - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_resp); data->pending_resp = resp; return NULL; } @@ -1641,7 +1641,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, "EAP-FAST: Could not derive keys"); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; - wpabuf_free(resp); + wpabuf_clear_free(resp); return NULL; } } @@ -1650,7 +1650,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, /* * Application data included in the handshake message. */ - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = resp; resp = NULL; res = eap_fast_decrypt(sm, data, ret, id, &msg, &resp); @@ -1658,7 +1658,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, } if (res == 1) { - wpabuf_free(resp); + wpabuf_clear_free(resp); return eap_peer_tls_build_ack(id, EAP_TYPE_FAST, data->fast_version); } @@ -1684,9 +1684,9 @@ static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv) data->phase2_method->deinit_for_reauth(sm, data->phase2_priv); os_free(data->key_block_p); data->key_block_p = NULL; - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = NULL; - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_resp); data->pending_resp = NULL; } diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c index 877495cf3ac72..249baec88ebb2 100644 --- a/src/eap_peer/eap_mschapv2.c +++ b/src/eap_peer/eap_mschapv2.c @@ -856,9 +856,13 @@ static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e., * peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */ - get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0); - get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, - MSCHAPV2_KEY_LEN, 0, 0); + if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, + 0) < 0 || + get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, + MSCHAPV2_KEY_LEN, 0, 0) < 0) { + os_free(key); + return NULL; + } wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, key_len); diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c index 34075b1d9af65..8dcf7cc2926f8 100644 --- a/src/eap_peer/eap_peap.c +++ b/src/eap_peer/eap_peap.c @@ -1,6 +1,6 @@ /* * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) - * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -169,7 +169,7 @@ static void * eap_peap_init(struct eap_sm *sm) static void eap_peap_free_key(struct eap_peap_data *data) { if (data->key_data) { - bin_clear_free(data->key_data, EAP_TLS_KEY_LEN); + bin_clear_free(data->key_data, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); data->key_data = NULL; } } @@ -186,9 +186,9 @@ static void eap_peap_deinit(struct eap_sm *sm, void *priv) eap_peer_tls_ssl_deinit(sm, &data->ssl); eap_peap_free_key(data); os_free(data->session_id); - wpabuf_free(data->pending_phase2_req); - wpabuf_free(data->pending_resp); - os_free(data); + wpabuf_clear_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_resp); + bin_clear_free(data, sizeof(*data)); } @@ -253,7 +253,7 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) { u8 *tk; u8 isk[32], imck[60]; - int resumed; + int resumed, res; /* * Tunnel key (TK) is the first 60 octets of the key generated by @@ -292,9 +292,11 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) * in the end of the label just before ISK; is that just a typo?) */ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); - if (peap_prfplus(data->peap_version, tk, 40, - "Inner Methods Compound Keys", - isk, sizeof(isk), imck, sizeof(imck)) < 0) + res = peap_prfplus(data->peap_version, tk, 40, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)); + os_memset(isk, 0, sizeof(isk)); + if (res < 0) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", imck, sizeof(imck)); @@ -303,6 +305,7 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); os_memcpy(data->cmk, imck + 40, 20); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); + os_memset(imck, 0, sizeof(imck)); return 0; } @@ -382,7 +385,7 @@ static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm, wpabuf_put_be16(msg, status); /* Status */ if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) { - wpabuf_free(msg); + wpabuf_clear_free(msg); return NULL; } @@ -651,11 +654,11 @@ static int eap_peap_phase2_request(struct eap_sm *sm, if (*resp == NULL) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; - wpabuf_free(buf); + wpabuf_clear_free(buf); return -1; } wpabuf_put_buf(*resp, buf); - wpabuf_free(buf); + wpabuf_clear_free(buf); break; } } @@ -728,7 +731,7 @@ static int eap_peap_phase2_request(struct eap_sm *sm, (config->pending_req_identity || config->pending_req_password || config->pending_req_otp || config->pending_req_new_password || config->pending_req_sim)) { - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); } @@ -807,7 +810,7 @@ continue_req: struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) + wpabuf_len(in_decrypted)); if (nmsg == NULL) { - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return 0; } nhdr = wpabuf_put(nmsg, sizeof(*nhdr)); @@ -817,7 +820,7 @@ continue_req: nhdr->length = host_to_be16(sizeof(struct eap_hdr) + wpabuf_len(in_decrypted)); - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); in_decrypted = nmsg; } @@ -826,7 +829,7 @@ continue_req: wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " "EAP frame (len=%lu)", (unsigned long) wpabuf_len(in_decrypted)); - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return 0; } len = be_to_host16(hdr->length); @@ -835,7 +838,7 @@ continue_req: "Phase 2 EAP frame (len=%lu hdr->length=%lu)", (unsigned long) wpabuf_len(in_decrypted), (unsigned long) len); - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return 0; } if (len < wpabuf_len(in_decrypted)) { @@ -852,7 +855,7 @@ continue_req: case EAP_CODE_REQUEST: if (eap_peap_phase2_request(sm, data, ret, in_decrypted, &resp)) { - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request " "processing failed"); return 0; @@ -872,7 +875,7 @@ continue_req: "completed successfully"); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return 0; } wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - " @@ -882,7 +885,7 @@ continue_req: ret->methodState = METHOD_DONE; data->phase2_success = 1; if (data->peap_outer_success == 2) { - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK " "to finish authentication"); return 1; @@ -928,7 +931,7 @@ continue_req: break; } - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); if (resp) { int skip_change2 = 0; @@ -955,7 +958,7 @@ continue_req: wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt " "a Phase 2 frame"); } - wpabuf_free(resp); + wpabuf_clear_free(resp); } return 0; @@ -1056,7 +1059,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, if (sm->waiting_ext_cert_check) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Waiting external server certificate validation"); - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_resp); data->pending_resp = resp; return NULL; } @@ -1081,12 +1084,19 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, "key derivation", label); data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, label, - EAP_TLS_KEY_LEN); + NULL, 0, + EAP_TLS_KEY_LEN + + EAP_EMSK_LEN); if (data->key_data) { wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Derived key", data->key_data, EAP_TLS_KEY_LEN); + wpa_hexdump_key(MSG_DEBUG, + "EAP-PEAP: Derived EMSK", + data->key_data + + EAP_TLS_KEY_LEN, + EAP_EMSK_LEN); } else { wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to " "derive key"); @@ -1131,7 +1141,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, /* * Application data included in the handshake message. */ - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = resp; resp = NULL; res = eap_peap_decrypt(sm, data, ret, req, &msg, @@ -1144,7 +1154,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, } if (res == 1) { - wpabuf_free(resp); + wpabuf_clear_free(resp); return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP, data->peap_version); } @@ -1168,9 +1178,9 @@ static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv) if (data->phase2_priv && data->phase2_method && data->phase2_method->deinit_for_reauth) data->phase2_method->deinit_for_reauth(sm, data->phase2_priv); - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = NULL; - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_resp); data->pending_resp = NULL; data->crypto_binding_used = 0; } @@ -1257,6 +1267,7 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) os_memcpy(key, csk, EAP_TLS_KEY_LEN); wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", key, EAP_TLS_KEY_LEN); + os_memset(csk, 0, sizeof(csk)); } else os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); @@ -1264,6 +1275,29 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_peap_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *key; + + if (!data->key_data || !data->phase2_success) + return NULL; + + if (data->crypto_binding_used) { + /* [MS-PEAP] does not define EMSK derivation */ + return NULL; + } + + key = os_memdup(data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); + if (!key) + return NULL; + + *len = EAP_EMSK_LEN; + + return key; +} + + static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) { struct eap_peap_data *data = priv; @@ -1296,6 +1330,7 @@ int eap_peer_peap_register(void) eap->process = eap_peap_process; eap->isKeyAvailable = eap_peap_isKeyAvailable; eap->getKey = eap_peap_getKey; + eap->get_emsk = eap_peap_get_emsk; eap->get_status = eap_peap_get_status; eap->has_reauth_data = eap_peap_has_reauth_data; eap->deinit_for_reauth = eap_peap_deinit_for_reauth; diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c index 761c16af996a3..76fcad4a50e0d 100644 --- a/src/eap_peer/eap_pwd.c +++ b/src/eap_peer/eap_pwd.c @@ -308,10 +308,10 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { - struct crypto_ec_point *K = NULL, *point = NULL; - struct crypto_bignum *mask = NULL, *cofactor = NULL; + struct crypto_ec_point *K = NULL; + struct crypto_bignum *mask = NULL; const u8 *ptr = payload; - u8 *scalar = NULL, *element = NULL; + u8 *scalar, *element; size_t prime_len, order_len; const u8 *password; size_t password_len; @@ -527,34 +527,17 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, data->private_value = crypto_bignum_init(); data->my_element = crypto_ec_point_init(data->grp->group); - cofactor = crypto_bignum_init(); data->my_scalar = crypto_bignum_init(); mask = crypto_bignum_init(); - if (!data->private_value || !data->my_element || !cofactor || + if (!data->private_value || !data->my_element || !data->my_scalar || !mask) { wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail"); goto fin; } - if (crypto_ec_cofactor(data->grp->group, cofactor) < 0) { - wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor " - "for curve"); + if (eap_pwd_get_rand_mask(data->grp, data->private_value, mask, + data->my_scalar) < 0) goto fin; - } - - if (crypto_bignum_rand(data->private_value, - crypto_ec_get_order(data->grp->group)) < 0 || - crypto_bignum_rand(mask, - crypto_ec_get_order(data->grp->group)) < 0 || - crypto_bignum_add(data->private_value, mask, - data->my_scalar) < 0 || - crypto_bignum_mod(data->my_scalar, - crypto_ec_get_order(data->grp->group), - data->my_scalar) < 0) { - wpa_printf(MSG_INFO, - "EAP-pwd (peer): unable to get randomness"); - goto fin; - } if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask, data->my_element) < 0) { @@ -572,43 +555,27 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, /* process the request */ data->k = crypto_bignum_init(); K = crypto_ec_point_init(data->grp->group); - point = crypto_ec_point_init(data->grp->group); - if (!data->k || !K || !point) { + if (!data->k || !K) { wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation " "fail"); goto fin; } /* element, x then y, followed by scalar */ - data->server_element = crypto_ec_point_from_bin(data->grp->group, ptr); + data->server_element = eap_pwd_get_element(data->grp, ptr); if (!data->server_element) { wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element " "fail"); goto fin; } ptr += prime_len * 2; - data->server_scalar = crypto_bignum_init_set(ptr, order_len); + data->server_scalar = eap_pwd_get_scalar(data->grp, ptr); if (!data->server_scalar) { wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer scalar fail"); goto fin; } - /* check to ensure server's element is not in a small sub-group */ - if (!crypto_bignum_is_one(cofactor)) { - if (crypto_ec_point_mul(data->grp->group, data->server_element, - cofactor, point) < 0) { - wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " - "server element by order!\n"); - goto fin; - } - if (crypto_ec_point_is_at_infinity(data->grp->group, point)) { - wpa_printf(MSG_INFO, "EAP-PWD (peer): server element " - "is at infinity!\n"); - goto fin; - } - } - /* compute the shared key, k */ if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, data->server_scalar, K) < 0 || @@ -621,17 +588,8 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; } - /* ensure that the shared key isn't in a small sub-group */ - if (!crypto_bignum_is_one(cofactor)) { - if (crypto_ec_point_mul(data->grp->group, K, cofactor, K) < 0) { - wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " - "shared key point by order"); - goto fin; - } - } - /* - * This check is strictly speaking just for the case above where + * This check is strictly speaking just for the case where * co-factor > 1 but it was suggested that even though this is probably * never going to happen it is a simple and safe check "just to be * sure" so let's be safe. @@ -649,12 +607,12 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, } /* now do the response */ - scalar = os_zalloc(order_len); - element = os_zalloc(prime_len * 2); - if (!scalar || !element) { - wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail"); + data->outbuf = wpabuf_alloc(2 * prime_len + order_len); + if (data->outbuf == NULL) goto fin; - } + /* We send the element as (x,y) followed by the scalar */ + element = wpabuf_put(data->outbuf, 2 * prime_len); + scalar = wpabuf_put(data->outbuf, order_len); /* * bignums occupy as little memory as possible so one that is @@ -668,21 +626,9 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; } - data->outbuf = wpabuf_alloc(order_len + 2 * prime_len); - if (data->outbuf == NULL) - goto fin; - - /* we send the element as (x,y) follwed by the scalar */ - wpabuf_put_data(data->outbuf, element, 2 * prime_len); - wpabuf_put_data(data->outbuf, scalar, order_len); - fin: - os_free(scalar); - os_free(element); crypto_bignum_deinit(mask, 1); - crypto_bignum_deinit(cofactor, 1); crypto_ec_point_deinit(K, 1); - crypto_ec_point_deinit(point, 1); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); else @@ -986,6 +932,13 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, * buffer and ACK the fragment */ if (EAP_PWD_GET_MORE_BIT(lm_exch) || data->in_frag_pos) { + if (!data->inbuf) { + wpa_printf(MSG_DEBUG, + "EAP-pwd: No buffer for reassembly"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } data->in_frag_pos += len; if (data->in_frag_pos > wpabuf_size(data->inbuf)) { wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack " @@ -1012,7 +965,7 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, /* * we're buffering and this is the last fragment */ - if (data->in_frag_pos) { + if (data->in_frag_pos && data->inbuf) { wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", (int) len); pos = wpabuf_head_u8(data->inbuf); diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c index 0a6ce255af4de..255241f6d5aa9 100644 --- a/src/eap_peer/eap_sake.c +++ b/src/eap_peer/eap_sake.c @@ -1,6 +1,6 @@ /* * EAP peer method: EAP-SAKE (RFC 4763) - * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -235,9 +235,13 @@ static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, data->serverid_len = attr.serverid_len; } - eap_sake_derive_keys(data->root_secret_a, data->root_secret_b, - data->rand_s, data->rand_p, - (u8 *) &data->tek, data->msk, data->emsk); + if (eap_sake_derive_keys(data->root_secret_a, data->root_secret_b, + data->rand_s, data->rand_p, + (u8 *) &data->tek, data->msk, + data->emsk) < 0) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to derive keys"); + return NULL; + } wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge"); diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c index cb747026cb8a5..ffea9d213855f 100644 --- a/src/eap_peer/eap_tls.c +++ b/src/eap_peer/eap_tls.c @@ -198,6 +198,7 @@ static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data, eap_tls_free_key(data); data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, label, + NULL, 0, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (data->key_data) { diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index 0de131526a519..cb94c452efceb 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -1,6 +1,6 @@ /* * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions - * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -70,16 +70,22 @@ static void eap_tls_params_flags(struct tls_connection_params *params, 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")) + if (os_strstr(txt, "tls_disable_tlsv1_0=0")) { params->flags &= ~TLS_CONN_DISABLE_TLSv1_0; + params->flags |= TLS_CONN_ENABLE_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")) + if (os_strstr(txt, "tls_disable_tlsv1_1=0")) { params->flags &= ~TLS_CONN_DISABLE_TLSv1_1; + params->flags |= TLS_CONN_ENABLE_TLSv1_1; + } if (os_strstr(txt, "tls_disable_tlsv1_2=1")) params->flags |= TLS_CONN_DISABLE_TLSv1_2; - if (os_strstr(txt, "tls_disable_tlsv1_2=0")) + if (os_strstr(txt, "tls_disable_tlsv1_2=0")) { params->flags &= ~TLS_CONN_DISABLE_TLSv1_2; + params->flags |= TLS_CONN_ENABLE_TLSv1_2; + } if (os_strstr(txt, "tls_disable_tlsv1_3=1")) params->flags |= TLS_CONN_DISABLE_TLSv1_3; if (os_strstr(txt, "tls_disable_tlsv1_3=0")) @@ -102,14 +108,15 @@ static void eap_tls_params_flags(struct tls_connection_params *params, static void eap_tls_params_from_conf1(struct tls_connection_params *params, struct eap_peer_config *config) { - params->ca_cert = (char *) config->ca_cert; - params->ca_path = (char *) config->ca_path; - params->client_cert = (char *) config->client_cert; - params->private_key = (char *) config->private_key; - params->private_key_passwd = (char *) config->private_key_passwd; - params->dh_file = (char *) config->dh_file; - params->subject_match = (char *) config->subject_match; - params->altsubject_match = (char *) config->altsubject_match; + params->ca_cert = config->ca_cert; + params->ca_path = config->ca_path; + params->client_cert = config->client_cert; + params->private_key = config->private_key; + params->private_key_passwd = config->private_key_passwd; + params->dh_file = config->dh_file; + params->subject_match = config->subject_match; + params->altsubject_match = config->altsubject_match; + params->check_cert_subject = config->check_cert_subject; params->suffix_match = config->domain_suffix_match; params->domain_match = config->domain_match; params->engine = config->engine; @@ -125,14 +132,15 @@ static void eap_tls_params_from_conf1(struct tls_connection_params *params, static void eap_tls_params_from_conf2(struct tls_connection_params *params, struct eap_peer_config *config) { - params->ca_cert = (char *) config->ca_cert2; - params->ca_path = (char *) config->ca_path2; - params->client_cert = (char *) config->client_cert2; - params->private_key = (char *) config->private_key2; - params->private_key_passwd = (char *) config->private_key2_passwd; - params->dh_file = (char *) config->dh_file2; - params->subject_match = (char *) config->subject_match2; - params->altsubject_match = (char *) config->altsubject_match2; + params->ca_cert = config->ca_cert2; + params->ca_path = config->ca_path2; + params->client_cert = config->client_cert2; + params->private_key = config->private_key2; + params->private_key_passwd = config->private_key2_passwd; + params->dh_file = config->dh_file2; + params->subject_match = config->subject_match2; + params->altsubject_match = config->altsubject_match2; + params->check_cert_subject = config->check_cert_subject2; params->suffix_match = config->domain_suffix_match2; params->domain_match = config->domain_match2; params->engine = config->engine2; @@ -170,7 +178,9 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, * TLS v1.3 changes, so disable this by default for now. */ params->flags |= TLS_CONN_DISABLE_TLSv1_3; } - if (data->eap_type == EAP_TYPE_TLS) { + if (data->eap_type == EAP_TYPE_TLS || + data->eap_type == EAP_UNAUTH_TLS_TYPE || + data->eap_type == EAP_WFA_UNAUTH_TLS_TYPE) { /* While the current EAP-TLS implementation is more or less * complete for TLS v1.3, there has been no interoperability * testing with other implementations, so disable for by default @@ -339,6 +349,8 @@ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @label: Label string for deriving the keys, e.g., "client EAP encryption" + * @context: Optional extra upper-layer context (max len 2^16) + * @context_len: The length of the context value * @len: Length of the key material to generate (usually 64 for MSK) * Returns: Pointer to allocated key on success or %NULL on failure * @@ -347,9 +359,12 @@ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) * different label to bind the key usage into the generated material. * * The caller is responsible for freeing the returned buffer. + * + * Note: To provide the RFC 5705 context, the context variable must be non-NULL. */ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, - const char *label, size_t len) + const char *label, const u8 *context, + size_t context_len, size_t len) { u8 *out; @@ -357,8 +372,8 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, if (out == NULL) return NULL; - if (tls_connection_export_key(data->ssl_ctx, data->conn, label, out, - len)) { + if (tls_connection_export_key(data->ssl_ctx, data->conn, label, + context, context_len, out, len)) { os_free(out); return NULL; } @@ -388,10 +403,26 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, u8 *out; if (eap_type == EAP_TYPE_TLS && data->tls_v13) { - *len = 64; - return eap_peer_tls_derive_key(sm, data, - "EXPORTER_EAP_TLS_Session-Id", - 64); + u8 *id, *method_id; + + /* Session-Id = <EAP-Type> || Method-Id + * Method-Id = TLS-Exporter("EXPORTER_EAP_TLS_Method-Id", + * "", 64) + */ + *len = 1 + 64; + id = os_malloc(*len); + if (!id) + return NULL; + method_id = eap_peer_tls_derive_key( + sm, data, "EXPORTER_EAP_TLS_Method-Id", NULL, 0, 64); + if (!method_id) { + os_free(id); + return NULL; + } + id[0] = eap_type; + os_memcpy(id + 1, method_id, 64); + os_free(method_id); + return id; } if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys) || diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h index 306e6a98bc3f7..5f825294d7871 100644 --- a/src/eap_peer/eap_tls_common.h +++ b/src/eap_peer/eap_tls_common.h @@ -99,7 +99,8 @@ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, struct eap_peer_config *config, u8 eap_type); 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); + const char *label, const u8 *context, + size_t context_len, size_t len); u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, struct eap_ssl_data *data, u8 eap_type, size_t *len); diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c index f18788ce8cb5c..1c8dbe2b43310 100644 --- a/src/eap_peer/eap_ttls.c +++ b/src/eap_peer/eap_ttls.c @@ -196,8 +196,8 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv) eap_peer_tls_ssl_deinit(sm, &data->ssl); eap_ttls_free_key(data); os_free(data->session_id); - wpabuf_free(data->pending_phase2_req); - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_resp); os_free(data); } @@ -248,7 +248,7 @@ static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code, msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4); if (msg == NULL) { - wpabuf_free(*resp); + wpabuf_clear_free(*resp); *resp = NULL; return -1; } @@ -258,7 +258,7 @@ static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code, os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp)); pos += wpabuf_len(*resp); AVP_PAD(avp, pos); - wpabuf_free(*resp); + wpabuf_clear_free(*resp); wpabuf_put(msg, pos - avp); *resp = msg; return 0; @@ -271,6 +271,7 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm, eap_ttls_free_key(data); data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, "ttls keying material", + NULL, 0, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (!data->key_data) { @@ -303,7 +304,8 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm, 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); + return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", + NULL, 0, len); } #endif /* CONFIG_FIPS */ @@ -510,7 +512,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, challenge = eap_ttls_implicit_challenge( sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); if (challenge == NULL) { - wpabuf_free(msg); + wpabuf_clear_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " "implicit challenge"); return -1; @@ -529,7 +531,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, *pos++ = 0; /* Flags */ if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) { os_free(challenge); - wpabuf_free(msg); + wpabuf_clear_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get " "random data for peer challenge"); return -1; @@ -543,7 +545,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, peer_challenge, pos, data->auth_response, data->master_key)) { os_free(challenge); - wpabuf_free(msg); + wpabuf_clear_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " "response"); return -1; @@ -604,7 +606,7 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, challenge = eap_ttls_implicit_challenge( sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); if (challenge == NULL) { - wpabuf_free(msg); + wpabuf_clear_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive " "implicit challenge"); return -1; @@ -628,7 +630,7 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, if (challenge_response(challenge, password, pos)) { wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed derive password hash"); - wpabuf_free(msg); + wpabuf_clear_free(msg); os_free(challenge); return -1; } @@ -641,7 +643,7 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, pos)) { wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed derive password"); - wpabuf_free(msg); + wpabuf_clear_free(msg); os_free(challenge); return -1; } @@ -760,7 +762,7 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm, challenge = eap_ttls_implicit_challenge( sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1); if (challenge == NULL) { - wpabuf_free(msg); + wpabuf_clear_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive " "implicit challenge"); return -1; @@ -1073,10 +1075,10 @@ static int eap_ttls_encrypt_response(struct eap_sm *sm, resp, out_data)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 " "frame"); - wpabuf_free(resp); + wpabuf_clear_free(resp); return -1; } - wpabuf_free(resp); + wpabuf_clear_free(resp); return 0; } @@ -1297,7 +1299,7 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm, config->pending_req_otp || config->pending_req_new_password || config->pending_req_sim) { - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_dup(in_decrypted); } @@ -1340,7 +1342,7 @@ static int eap_ttls_implicit_identity_request(struct eap_sm *sm, * processing when EAP request is re-processed after * user input. */ - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_alloc(0); } @@ -1413,7 +1415,7 @@ static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, in_decrypted = data->pending_phase2_req; data->pending_phase2_req = NULL; if (wpabuf_len(in_decrypted) == 0) { - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return eap_ttls_implicit_identity_request( sm, data, ret, identifier, out_data); } @@ -1449,7 +1451,7 @@ continue_req: &parse, in_decrypted, out_data); done: - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); os_free(parse.eapdata); if (retval < 0) { @@ -1509,7 +1511,7 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, if (sm->waiting_ext_cert_check) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Waiting external server certificate validation"); - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_resp); data->pending_resp = *out_data; *out_data = NULL; return 0; @@ -1543,7 +1545,7 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, /* * Application data included in the handshake message. */ - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = *out_data; *out_data = NULL; res = eap_ttls_decrypt(sm, data, ret, identifier, in_data, @@ -1646,7 +1648,7 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, /* FIX: what about res == -1? Could just move all error processing into * the other functions and get rid of this res==1 case here. */ if (res == 1) { - wpabuf_free(resp); + wpabuf_clear_free(resp); return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS, data->ttls_version); } @@ -1669,9 +1671,9 @@ static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv) if (data->phase2_priv && data->phase2_method && data->phase2_method->deinit_for_reauth) data->phase2_method->deinit_for_reauth(sm, data->phase2_priv); - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = NULL; - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_resp); data->pending_resp = NULL; data->decision_succ = DECISION_FAIL; #ifdef EAP_TNC diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c index d140c88b8cdd8..92d5a02351304 100644 --- a/src/eap_peer/eap_wsc.c +++ b/src/eap_peer/eap_wsc.c @@ -255,6 +255,9 @@ static void * eap_wsc_init(struct eap_sm *sm) cfg.new_ap_settings = &new_ap_settings; } + if (os_strstr(phase1, "multi_ap=1")) + cfg.multi_ap_backhaul_sta = 1; + data->wps = wps_init(&cfg); if (data->wps == NULL) { os_free(data); diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h index 4fbc661c22fe2..b130368b64da2 100644 --- a/src/eap_server/eap.h +++ b/src/eap_server/eap.h @@ -153,11 +153,14 @@ void eap_sm_pending_cb(struct eap_sm *sm); int eap_sm_method_pending(struct eap_sm *sm); const u8 * eap_get_identity(struct eap_sm *sm, size_t *len); const char * eap_get_serial_num(struct eap_sm *sm); +const char * eap_get_method(struct eap_sm *sm); +const char * eap_get_imsi(struct eap_sm *sm); struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm); void eap_server_clear_identity(struct eap_sm *sm); void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source, const u8 *username, size_t username_len, const u8 *challenge, const u8 *response); void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len); +void eap_user_free(struct eap_user *user); #endif /* EAP_H */ diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h index cf8a9f0d98e1f..1cade10bee554 100644 --- a/src/eap_server/eap_i.h +++ b/src/eap_server/eap_i.h @@ -160,6 +160,7 @@ struct eap_sm { u8 *identity; size_t identity_len; char *serial_num; + char imsi[20]; /* Whether Phase 2 method should validate identity match */ int require_identity_match; int lastId; /* Identifier used in the last EAP-Packet */ diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c index 38a1b5c9ee220..e8b36e13380db 100644 --- a/src/eap_server/eap_server.c +++ b/src/eap_server/eap_server.c @@ -25,9 +25,6 @@ #define EAP_MAX_AUTH_ROUNDS 50 -static void eap_user_free(struct eap_user *user); - - /* EAP state machines are described in RFC 4137 */ static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, @@ -1814,7 +1811,7 @@ int eap_server_sm_step(struct eap_sm *sm) } -static void eap_user_free(struct eap_user *user) +void eap_user_free(struct eap_user *user) { if (user == NULL) return; @@ -2003,6 +2000,32 @@ const char * eap_get_serial_num(struct eap_sm *sm) } +/** + * eap_get_method - Get the used EAP method + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * Returns: Pointer to the method name or %NULL if not available + */ +const char * eap_get_method(struct eap_sm *sm) +{ + if (!sm || !sm->m) + return NULL; + return sm->m->name; +} + + +/** + * eap_get_imsi - Get IMSI of the user + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * Returns: Pointer to IMSI or %NULL if not available + */ +const char * eap_get_imsi(struct eap_sm *sm) +{ + if (!sm || sm->imsi[0] == '\0') + return NULL; + return sm->imsi; +} + + void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len) { #ifdef CONFIG_ERP diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c index 175021163c1df..1bea706d4990e 100644 --- a/src/eap_server/eap_server_aka.c +++ b/src/eap_server/eap_server_aka.c @@ -796,6 +796,10 @@ static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data) return; } + if (data->permanent[0] == EAP_AKA_PERMANENT_PREFIX || + data->permanent[0] == EAP_AKA_PRIME_PERMANENT_PREFIX) + os_strlcpy(sm->imsi, &data->permanent[1], sizeof(sm->imsi)); + #ifdef EAP_SERVER_AKA_PRIME if (data->eap_method == EAP_TYPE_AKA_PRIME) { /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c index fb3d11748c8c2..bebb17f40aaa5 100644 --- a/src/eap_server/eap_server_gpsk.c +++ b/src/eap_server/eap_server_gpsk.c @@ -181,7 +181,7 @@ static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm, if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, data->specifier, start, pos - start, pos) < 0) { - os_free(req); + wpabuf_free(req); eap_gpsk_state(data, FAILURE); return NULL; } @@ -379,7 +379,7 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, data->specifier = WPA_GET_BE16(csuite->specifier); wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel %d:%d", data->vendor, data->specifier); - pos += sizeof(*csuite); + pos += sizeof(*csuite); if (end - pos < 2) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c index 6c47bb636aab9..e9e03b0afb452 100644 --- a/src/eap_server/eap_server_mschapv2.c +++ b/src/eap_server/eap_server_mschapv2.c @@ -551,9 +551,13 @@ static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) if (key == NULL) return NULL; /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */ - get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1); - get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, - MSCHAPV2_KEY_LEN, 1, 1); + if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, + 1) < 0 || + get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, + MSCHAPV2_KEY_LEN, 1, 1) < 0) { + os_free(key); + return NULL; + } wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len); return key; diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c index 3257789695cde..2e8c1a60c71fe 100644 --- a/src/eap_server/eap_server_pax.c +++ b/src/eap_server/eap_server_pax.c @@ -107,9 +107,14 @@ static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm, data->rand.r.x, EAP_PAX_RAND_LEN); pos = wpabuf_put(req, EAP_PAX_MAC_LEN); - eap_pax_mac(data->mac_id, (u8 *) "", 0, - wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, - NULL, 0, NULL, 0, pos); + if (eap_pax_mac(data->mac_id, (u8 *) "", 0, + wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, pos) < 0) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV"); + data->state = FAILURE; + wpabuf_free(req); + return NULL; + } wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); return req; @@ -144,18 +149,28 @@ static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm, wpabuf_put_be16(req, EAP_PAX_MAC_LEN); pos = wpabuf_put(req, EAP_PAX_MAC_LEN); - eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, - data->rand.r.y, EAP_PAX_RAND_LEN, - (u8 *) data->cid, data->cid_len, NULL, 0, pos); + if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, + data->rand.r.y, EAP_PAX_RAND_LEN, + (u8 *) data->cid, data->cid_len, NULL, 0, pos) < 0) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate MAC"); + data->state = FAILURE; + wpabuf_free(req); + return NULL; + } wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", pos, EAP_PAX_MAC_LEN); /* Optional ADE could be added here, if needed */ pos = wpabuf_put(req, EAP_PAX_MAC_LEN); - eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, - wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, - NULL, 0, NULL, 0, pos); + if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, pos) < 0) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV"); + data->state = FAILURE; + wpabuf_free(req); + return NULL; + } wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); return req; @@ -190,7 +205,7 @@ static Boolean eap_pax_check(struct eap_sm *sm, void *priv, u8 icvbuf[EAP_PAX_ICV_LEN], *icv; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); - if (pos == NULL || len < sizeof(*resp)) { + if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame"); return TRUE; } @@ -264,11 +279,11 @@ static Boolean eap_pax_check(struct eap_sm *sm, void *priv, } icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN; wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); - eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, - wpabuf_mhead(respData), - wpabuf_len(respData) - EAP_PAX_ICV_LEN, - NULL, 0, NULL, 0, icvbuf); - if (os_memcmp_const(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { + if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_mhead(respData), + wpabuf_len(respData) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, icvbuf) < 0 || + os_memcmp_const(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", icvbuf, EAP_PAX_ICV_LEN); @@ -395,11 +410,11 @@ static void eap_pax_process_std_2(struct eap_sm *sm, } data->keys_set = 1; - eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, - data->rand.r.x, EAP_PAX_RAND_LEN, - data->rand.r.y, EAP_PAX_RAND_LEN, - (u8 *) data->cid, data->cid_len, mac); - if (os_memcmp_const(mac, pos, EAP_PAX_MAC_LEN) != 0) { + if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, + data->rand.r.x, EAP_PAX_RAND_LEN, + data->rand.r.y, EAP_PAX_RAND_LEN, + (u8 *) data->cid, data->cid_len, mac) < 0 || + os_memcmp_const(mac, pos, EAP_PAX_MAC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in " "PAX_STD-2"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)", @@ -417,11 +432,11 @@ static void eap_pax_process_std_2(struct eap_sm *sm, return; } wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); - eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, - wpabuf_head(respData), - wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, - icvbuf); - if (os_memcmp_const(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { + if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_head(respData), + wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, + NULL, 0, icvbuf) < 0 || + os_memcmp_const(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", icvbuf, EAP_PAX_ICV_LEN); diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c index 18d31b527fdd7..92c0e5ec97163 100644 --- a/src/eap_server/eap_server_peap.c +++ b/src/eap_server/eap_server_peap.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -324,13 +324,14 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) { u8 *tk; u8 isk[32], imck[60]; + int res; /* * Tunnel key (TK) is the first 60 octets of the key generated by * phase 1 of PEAP (based on TLS). */ tk = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption", - EAP_TLS_KEY_LEN); + NULL, 0, EAP_TLS_KEY_LEN); if (tk == NULL) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); @@ -358,9 +359,11 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) * in the end of the label just before ISK; is that just a typo?) */ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); - if (peap_prfplus(data->peap_version, tk, 40, - "Inner Methods Compound Keys", - isk, sizeof(isk), imck, sizeof(imck)) < 0) { + res = peap_prfplus(data->peap_version, tk, 40, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)); + os_memset(isk, 0, sizeof(isk)); + if (res < 0) { os_free(tk); return -1; } @@ -373,6 +376,7 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); os_memcpy(data->cmk, imck + 40, 20); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); + os_memset(imck, 0, sizeof(imck)); return 0; } @@ -756,7 +760,7 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm, } else { eap_peap_state(data, FAILURE); } - + } else if (status == EAP_TLV_RESULT_FAILURE) { wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Failure " "- requested %s", requested); @@ -1322,14 +1326,17 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) "key"); } + os_memset(csk, 0, sizeof(csk)); + return eapKeyData; } /* TODO: PEAPv1 - different label in some cases */ eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "client EAP encryption", - EAP_TLS_KEY_LEN); + "client EAP encryption", NULL, 0, + EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { + os_memset(eapKeyData + EAP_TLS_KEY_LEN, 0, EAP_EMSK_LEN); *len = EAP_TLS_KEY_LEN; wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", eapKeyData, EAP_TLS_KEY_LEN); @@ -1341,6 +1348,40 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_peap_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *eapKeyData, *emsk; + + if (data->state != SUCCESS) + return NULL; + + if (data->crypto_binding_used) { + /* [MS-PEAP] does not define EMSK derivation */ + return NULL; + } + + /* TODO: PEAPv1 - different label in some cases */ + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, + "client EAP encryption", NULL, 0, + EAP_TLS_KEY_LEN + EAP_EMSK_LEN); + if (eapKeyData) { + emsk = os_memdup(eapKeyData + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); + bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); + if (!emsk) + return NULL; + *len = EAP_EMSK_LEN; + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived EMSK", + emsk, EAP_EMSK_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive EMSK"); + emsk = NULL; + } + + return emsk; +} + + static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv) { struct eap_peap_data *data = priv; @@ -1376,6 +1417,7 @@ int eap_server_peap_register(void) eap->process = eap_peap_process; eap->isDone = eap_peap_isDone; eap->getKey = eap_peap_getKey; + eap->get_emsk = eap_peap_get_emsk; eap->isSuccess = eap_peap_isSuccess; eap->getSessionId = eap_peap_get_session_id; diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c index d0fa54a3aba7d..e720a28c85bac 100644 --- a/src/eap_server/eap_server_pwd.c +++ b/src/eap_server/eap_server_pwd.c @@ -236,7 +236,7 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) { struct crypto_bignum *mask = NULL; - u8 *scalar = NULL, *element = NULL; + u8 *scalar, *element; size_t prime_len, order_len; wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request"); @@ -261,18 +261,9 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, goto fin; } - if (crypto_bignum_rand(data->private_value, - crypto_ec_get_order(data->grp->group)) < 0 || - crypto_bignum_rand(mask, - crypto_ec_get_order(data->grp->group)) < 0 || - crypto_bignum_add(data->private_value, mask, data->my_scalar) < 0 || - crypto_bignum_mod(data->my_scalar, - crypto_ec_get_order(data->grp->group), - data->my_scalar) < 0) { - wpa_printf(MSG_INFO, - "EAP-pwd (server): unable to get randomness"); + if (eap_pwd_get_rand_mask(data->grp, data->private_value, mask, + data->my_scalar) < 0) goto fin; - } if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask, data->my_element) < 0) { @@ -288,22 +279,6 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, goto fin; } - scalar = os_malloc(order_len); - element = os_malloc(prime_len * 2); - if (!scalar || !element) { - wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail"); - goto fin; - } - - if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element, - element + prime_len) < 0) { - wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment " - "fail"); - goto fin; - } - - crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len); - data->outbuf = wpabuf_alloc(2 * prime_len + order_len + (data->salt ? 1 + data->salt_len : 0)); if (data->outbuf == NULL) @@ -316,13 +291,18 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, } /* We send the element as (x,y) followed by the scalar */ - wpabuf_put_data(data->outbuf, element, 2 * prime_len); - wpabuf_put_data(data->outbuf, scalar, order_len); + element = wpabuf_put(data->outbuf, 2 * prime_len); + scalar = wpabuf_put(data->outbuf, order_len); + crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len); + if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element, + element + prime_len) < 0) { + wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment " + "fail"); + goto fin; + } fin: crypto_bignum_deinit(mask, 1); - os_free(scalar); - os_free(element); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); } @@ -331,7 +311,7 @@ fin: static void eap_pwd_build_confirm_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) { - struct crypto_hash *hash; + struct crypto_hash *hash = NULL; u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; u16 grp; size_t prime_len, order_len; @@ -412,6 +392,7 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm, /* all done with the random function */ eap_pwd_h_final(hash, conf); + hash = NULL; os_memcpy(data->my_confirm, conf, SHA256_MAC_LEN); data->outbuf = wpabuf_alloc(SHA256_MAC_LEN); @@ -424,6 +405,7 @@ fin: bin_clear_free(cruft, prime_len * 2); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); + eap_pwd_h_final(hash, NULL); } @@ -668,8 +650,7 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { const u8 *ptr; - struct crypto_bignum *cofactor = NULL; - struct crypto_ec_point *K = NULL, *point = NULL; + struct crypto_ec_point *K = NULL; int res = 0; size_t prime_len, order_len; @@ -687,50 +668,36 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, } data->k = crypto_bignum_init(); - cofactor = crypto_bignum_init(); - point = crypto_ec_point_init(data->grp->group); K = crypto_ec_point_init(data->grp->group); - if (!data->k || !cofactor || !point || !K) { + if (!data->k || !K) { wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation " "fail"); goto fin; } - if (crypto_ec_cofactor(data->grp->group, cofactor) < 0) { - wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get " - "cofactor for curve"); - goto fin; - } - /* element, x then y, followed by scalar */ ptr = payload; - data->peer_element = crypto_ec_point_from_bin(data->grp->group, ptr); + data->peer_element = eap_pwd_get_element(data->grp, ptr); if (!data->peer_element) { wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element " "fail"); goto fin; } ptr += prime_len * 2; - data->peer_scalar = crypto_bignum_init_set(ptr, order_len); + data->peer_scalar = eap_pwd_get_scalar(data->grp, ptr); if (!data->peer_scalar) { wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation " "fail"); goto fin; } - /* check to ensure peer's element is not in a small sub-group */ - if (!crypto_bignum_is_one(cofactor)) { - if (crypto_ec_point_mul(data->grp->group, data->peer_element, - cofactor, point) != 0) { - wpa_printf(MSG_INFO, "EAP-PWD (server): cannot " - "multiply peer element by order"); - goto fin; - } - if (crypto_ec_point_is_at_infinity(data->grp->group, point)) { - wpa_printf(MSG_INFO, "EAP-PWD (server): peer element " - "is at infinity!\n"); - goto fin; - } + /* detect reflection attacks */ + if (crypto_bignum_cmp(data->my_scalar, data->peer_scalar) == 0 || + crypto_ec_point_cmp(data->grp->group, data->my_element, + data->peer_element) == 0) { + wpa_printf(MSG_INFO, + "EAP-PWD (server): detected reflection attack!"); + goto fin; } /* compute the shared key, k */ @@ -745,18 +712,8 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; } - /* ensure that the shared key isn't in a small sub-group */ - if (!crypto_bignum_is_one(cofactor)) { - if (crypto_ec_point_mul(data->grp->group, K, cofactor, - K) != 0) { - wpa_printf(MSG_INFO, "EAP-PWD (server): cannot " - "multiply shared key point by order!\n"); - goto fin; - } - } - /* - * This check is strictly speaking just for the case above where + * This check is strictly speaking just for the case where * co-factor > 1 but it was suggested that even though this is probably * never going to happen it is a simple and safe check "just to be * sure" so let's be safe. @@ -775,8 +732,6 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, fin: crypto_ec_point_deinit(K, 1); - crypto_ec_point_deinit(point, 1); - crypto_bignum_deinit(cofactor, 1); if (res) eap_pwd_state(data, PWD_Confirm_Req); @@ -789,7 +744,7 @@ static void eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { - struct crypto_hash *hash; + struct crypto_hash *hash = NULL; u32 cs; u16 grp; u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; @@ -864,6 +819,7 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, /* all done */ eap_pwd_h_final(hash, conf); + hash = NULL; ptr = (u8 *) payload; if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) { @@ -883,6 +839,7 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, fin: bin_clear_free(cruft, prime_len * 2); + eap_pwd_h_final(hash, NULL); } @@ -955,6 +912,12 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv, * the first and all intermediate fragments have the M bit set */ if (EAP_PWD_GET_MORE_BIT(lm_exch) || data->in_frag_pos) { + if (!data->inbuf) { + wpa_printf(MSG_DEBUG, + "EAP-pwd: No buffer for reassembly"); + eap_pwd_state(data, FAILURE); + return; + } if ((data->in_frag_pos + len) > wpabuf_size(data->inbuf)) { wpa_printf(MSG_DEBUG, "EAP-pwd: Buffer overflow " "attack detected! (%d+%d > %d)", @@ -975,7 +938,7 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv, * last fragment won't have the M bit set (but we're obviously * buffering fragments so that's how we know it's the last) */ - if (data->in_frag_pos) { + if (data->in_frag_pos && data->inbuf) { pos = wpabuf_head_u8(data->inbuf); len = data->in_frag_pos; wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", @@ -1075,14 +1038,6 @@ static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_pwd_register(void) { struct eap_method *eap; - struct timeval tp; - struct timezone tz; - u32 sr; - - sr = 0xdeaddada; - (void) gettimeofday(&tp, &tz); - sr ^= (tp.tv_sec ^ tp.tv_usec); - srandom(sr); eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PWD, diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c index 66183f5f5c042..2fc2c0575a94e 100644 --- a/src/eap_server/eap_server_sake.c +++ b/src/eap_server/eap_server_sake.c @@ -204,7 +204,7 @@ static struct wpabuf * eap_sake_build_confirm(struct eap_sm *sm, { wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); data->state = FAILURE; - os_free(msg); + wpabuf_free(msg); return NULL; } @@ -340,16 +340,25 @@ static void eap_sake_process_challenge(struct eap_sm *sm, data->state = FAILURE; return; } - eap_sake_derive_keys(sm->user->password, - sm->user->password + EAP_SAKE_ROOT_SECRET_LEN, - data->rand_s, data->rand_p, - (u8 *) &data->tek, data->msk, data->emsk); - - eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, - sm->server_id, sm->server_id_len, - data->peerid, data->peerid_len, 1, - wpabuf_head(respData), wpabuf_len(respData), - attr.mic_p, mic_p); + if (eap_sake_derive_keys(sm->user->password, + sm->user->password + EAP_SAKE_ROOT_SECRET_LEN, + data->rand_s, data->rand_p, + (u8 *) &data->tek, data->msk, + data->emsk) < 0) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to derive keys"); + data->state = FAILURE; + return; + } + + if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, 1, + wpabuf_head(respData), wpabuf_len(respData), + attr.mic_p, mic_p) < 0) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); + data->state = FAILURE; + return; + } if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P"); eap_sake_state(data, FAILURE); @@ -382,11 +391,14 @@ static void eap_sake_process_confirm(struct eap_sm *sm, return; } - eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, - sm->server_id, sm->server_id_len, - data->peerid, data->peerid_len, 1, - wpabuf_head(respData), wpabuf_len(respData), - attr.mic_p, mic_p); + if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, 1, + wpabuf_head(respData), wpabuf_len(respData), + attr.mic_p, mic_p) < 0) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); + return; + } if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P"); eap_sake_state(data, FAILURE); diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c index 10637d4c66b90..128782735fb38 100644 --- a/src/eap_server/eap_server_sim.c +++ b/src/eap_server/eap_server_sim.c @@ -535,6 +535,9 @@ skip_id_update: goto failed; } + if (data->permanent[0] == EAP_SIM_PERMANENT_PREFIX) + os_strlcpy(sm->imsi, &data->permanent[1], sizeof(sm->imsi)); + identity_len = sm->identity_len; while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') { wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop last null " diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c index 8b9e53c61d799..357e72a825f6d 100644 --- a/src/eap_server/eap_server_tls.c +++ b/src/eap_server/eap_server_tls.c @@ -22,6 +22,7 @@ struct eap_tls_data { enum { START, CONTINUE, SUCCESS, FAILURE } state; int established; u8 eap_type; + int phase2; }; @@ -85,6 +86,8 @@ static void * eap_tls_init(struct eap_sm *sm) data->eap_type = EAP_TYPE_TLS; + data->phase2 = sm->init_phase2; + return data; } @@ -202,6 +205,20 @@ check_established: wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); eap_tls_state(data, SUCCESS); eap_tls_valid_session(sm, data); + if (sm->serial_num) { + char user[128]; + int user_len; + + user_len = os_snprintf(user, sizeof(user), "cert-%s", + sm->serial_num); + if (eap_user_get(sm, (const u8 *) user, user_len, + data->phase2) < 0) + wpa_printf(MSG_DEBUG, + "EAP-TLS: No user entry found based on the serial number of the client certificate "); + else + wpa_printf(MSG_DEBUG, + "EAP-TLS: Updated user entry based on the serial number of the client certificate "); + } } return res; @@ -288,6 +305,8 @@ static void eap_tls_process(struct eap_sm *sm, void *priv, "EAP-TLS: Resuming previous session"); eap_tls_state(data, SUCCESS); tls_connection_set_success_data_resumed(data->ssl.conn); + /* TODO: Cache serial number with session and update EAP user + * information based on the cached serial number */ } @@ -312,6 +331,7 @@ static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) else label = "client EAP encryption"; eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label, + NULL, 0, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { *len = EAP_TLS_KEY_LEN; @@ -340,6 +360,7 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) else label = "client EAP encryption"; eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label, + NULL, 0, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { emsk = os_malloc(EAP_EMSK_LEN); diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c index 0ae7867fccf7f..0eca0ff774094 100644 --- a/src/eap_server/eap_server_tls_common.c +++ b/src/eap_server/eap_server_tls_common.c @@ -107,7 +107,8 @@ 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, - const char *label, size_t len) + const char *label, const u8 *context, + size_t context_len, size_t len) { u8 *out; @@ -115,8 +116,8 @@ u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, if (out == NULL) return NULL; - if (tls_connection_export_key(sm->ssl_ctx, data->conn, label, out, - len)) { + if (tls_connection_export_key(sm->ssl_ctx, data->conn, label, + context, context_len, out, len)) { os_free(out); return NULL; } @@ -146,10 +147,26 @@ u8 * eap_server_tls_derive_session_id(struct eap_sm *sm, u8 *out; if (eap_type == EAP_TYPE_TLS && data->tls_v13) { - *len = 64; - return eap_server_tls_derive_key(sm, data, - "EXPORTER_EAP_TLS_Session-Id", - 64); + u8 *id, *method_id; + + /* Session-Id = <EAP-Type> || Method-Id + * Method-Id = TLS-Exporter("EXPORTER_EAP_TLS_Method-Id", + * "", 64) + */ + *len = 1 + 64; + id = os_malloc(*len); + if (!id) + return NULL; + method_id = eap_server_tls_derive_key( + sm, data, "EXPORTER_EAP_TLS_Method-Id", NULL, 0, 64); + if (!method_id) { + os_free(id); + return NULL; + } + id[0] = eap_type; + os_memcpy(id + 1, method_id, 64); + os_free(method_id); + return id; } if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys)) diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c index b14996b0b9909..52bff8afe42d8 100644 --- a/src/eap_server/eap_server_ttls.c +++ b/src/eap_server/eap_server_ttls.c @@ -332,7 +332,7 @@ static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, struct eap_ttls_data *data, size_t len) { return eap_server_tls_derive_key(sm, &data->ssl, "ttls challenge", - len); + NULL, 0, len); } @@ -1268,7 +1268,7 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) return NULL; eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "ttls keying material", + "ttls keying material", NULL, 0, EAP_TLS_KEY_LEN); if (eapKeyData) { *len = EAP_TLS_KEY_LEN; @@ -1310,7 +1310,7 @@ static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) return NULL; eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "ttls keying material", + "ttls keying material", NULL, 0, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { emsk = os_malloc(EAP_EMSK_LEN); diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h index 31f6e72d779a1..0b04983adb0e9 100644 --- a/src/eap_server/eap_tls_common.h +++ b/src/eap_server/eap_tls_common.h @@ -78,7 +78,8 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, int verify_peer, int eap_type); void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, - const char *label, size_t len); + const char *label, const u8 *context, + size_t context_len, size_t len); u8 * eap_server_tls_derive_session_id(struct eap_sm *sm, struct eap_ssl_data *data, u8 eap_type, size_t *len); diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index 9f029b0d3710f..a0f27fd2bdb29 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -189,8 +189,9 @@ static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) } if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) { - eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, - sm); + if (eloop_register_timeout(1, 0, eapol_port_timers_tick, + eloop_ctx, sm) < 0) + sm->timer_tick_enabled = 0; } else { wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick"); sm->timer_tick_enabled = 0; @@ -204,9 +205,9 @@ static void eapol_enable_timer_tick(struct eapol_sm *sm) if (sm->timer_tick_enabled) return; wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick"); - sm->timer_tick_enabled = 1; eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); - eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); + if (eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm) == 0) + sm->timer_tick_enabled = 1; } @@ -2141,8 +2142,8 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) sm->initialize = FALSE; eapol_sm_step(sm); - sm->timer_tick_enabled = 1; - eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); + if (eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm) == 0) + sm->timer_tick_enabled = 1; return sm; } diff --git a/src/fst/fst.h b/src/fst/fst.h index 0c0e435b974b5..296749120b2a1 100644 --- a/src/fst/fst.h +++ b/src/fst/fst.h @@ -19,10 +19,18 @@ #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) +/* + * These were originally + * #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) + * but those can overflow 32-bit unsigned integer, so use alternative defines + * to avoid undefined behavior with such overflow. + * LLT_UNIT_US/US_IN_MS = 32/1000 = 4/125 + */ +#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * 125 / 4) +#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * 4 / 125) +#define FST_MAX_LLT_MS (((u32) -1) / 4) #define FST_MAX_PRIO_VALUE ((u8) -1) #define FST_MAX_GROUP_ID_LEN IFNAMSIZ diff --git a/src/lib.rules b/src/lib.rules index 0c79d992a6aa4..4ec4711e36acf 100644 --- a/src/lib.rules +++ b/src/lib.rules @@ -6,6 +6,11 @@ ifndef CFLAGS CFLAGS = -MMD -O2 -Wall -g endif +ifdef TEST_FUZZ +CFLAGS += -DCONFIG_NO_RANDOM_POOL +CFLAGS += -DTEST_FUZZ +endif + CFLAGS += -I.. -I../utils diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index b4660c4f9616e..157bf891c4ab8 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -1076,7 +1076,7 @@ static int p2p_run_after_scan(struct p2p_data *p2p) p2p->after_scan_tx->bssid, (u8 *) (p2p->after_scan_tx + 1), p2p->after_scan_tx->len, - p2p->after_scan_tx->wait_time); + p2p->after_scan_tx->wait_time, NULL); os_free(p2p->after_scan_tx); p2p->after_scan_tx = NULL; return 1; @@ -1172,9 +1172,9 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, u8 seek_count, const char **seek, int freq) { int res; + struct os_reltime start; p2p_dbg(p2p, "Starting find (type=%d)", type); - os_get_reltime(&p2p->find_start); if (p2p->p2p_scan_running) { p2p_dbg(p2p, "p2p_scan is already running"); } @@ -1258,6 +1258,7 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, if (timeout) eloop_register_timeout(timeout, 0, p2p_find_timeout, p2p, NULL); + os_get_reltime(&start); switch (type) { case P2P_FIND_START_WITH_FULL: if (freq > 0) { @@ -1289,6 +1290,9 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, return -1; } + if (!res) + p2p->find_start = start; + if (res != 0 && p2p->p2p_scan_running) { p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running"); /* wait for the previous p2p_scan to complete */ @@ -1470,7 +1474,8 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p) p2p->op_channel = p2p->cfg->op_channel; } else if (p2p_channel_random_social(&p2p->cfg->channels, &p2p->op_reg_class, - &p2p->op_channel) == 0) { + &p2p->op_channel, + NULL, NULL) == 0) { p2p_dbg(p2p, "Select random available social channel (op_class %u channel %u) as operating channel preference", p2p->op_reg_class, p2p->op_channel); } else { @@ -2456,7 +2461,7 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, if (msg.wps_attributes && !p2p_match_dev_type(p2p, msg.wps_attributes)) { /* No match with Requested Device Type */ - p2p_dbg(p2p, "Probe Req requestred Device Type did not match - ignore it"); + p2p_dbg(p2p, "Probe Req requested Device Type did not match - ignore it"); p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } @@ -4764,9 +4769,12 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled) int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class, - u8 *op_channel) + u8 *op_channel, + struct wpa_freq_range_list *avoid_list, + struct wpa_freq_range_list *disallow_list) { - return p2p_channel_random_social(&p2p->channels, op_class, op_channel); + return p2p_channel_random_social(&p2p->channels, op_class, op_channel, + avoid_list, disallow_list); } @@ -4965,6 +4973,8 @@ int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, size_t len, unsigned int wait_time) { + int res, scheduled; + if (p2p->p2p_scan_running) { p2p_dbg(p2p, "Delay Action frame TX until p2p_scan completes"); if (p2p->after_scan_tx) { @@ -4985,8 +4995,16 @@ int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, return 0; } - return p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid, - buf, len, wait_time); + res = p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid, + buf, len, wait_time, &scheduled); + if (res == 0 && scheduled && p2p->in_listen && freq > 0 && + (unsigned int) p2p->drv_in_listen != freq) { + p2p_dbg(p2p, + "Stop listen on %d MHz to allow a frame to be sent immediately on %d MHz", + p2p->drv_in_listen, freq); + p2p_stop_listen_for_freq(p2p, freq); + } + return res; } diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index fac5ce05ae6e1..425b037be5158 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -104,6 +104,11 @@ struct p2p_go_neg_results { unsigned int vht_center_freq2; /** + * he - Indicates if IEEE 802.11ax HE is enabled + */ + int he; + + /** * ssid - SSID of the group */ u8 ssid[SSID_MAX_LEN]; @@ -660,6 +665,8 @@ struct p2p_config { * @buf: Frame body (starting from Category field) * @len: Length of buf in octets * @wait_time: How many msec to wait for a response frame + * @scheduled: Return value indicating whether the transmissions was + * scheduled to happen once the radio is available * Returns: 0 on success, -1 on failure * * The Action frame may not be transmitted immediately and the status @@ -670,7 +677,7 @@ struct p2p_config { */ int (*send_action)(void *ctx, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, - size_t len, unsigned int wait_time); + size_t len, unsigned int wait_time, int *scheduled); /** * send_action_done - Notify that Action frame sequence was completed @@ -2005,6 +2012,8 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled); * @p2p: P2P config * @op_class: Selected operating class * @op_channel: Selected social channel + * @avoid_list: Channel ranges to try to avoid or %NULL + * @disallow_list: Channel ranges to discard or %NULL * Returns: 0 on success, -1 on failure * * This function is used before p2p_init is called. A random social channel @@ -2012,7 +2021,9 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled); * returned on success. */ int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class, - u8 *op_channel); + u8 *op_channel, + struct wpa_freq_range_list *avoid_list, + struct wpa_freq_range_list *disallow_list); int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel, u8 forced); diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c index 2882c6ad02e7e..63eb2e84c3762 100644 --- a/src/p2p/p2p_build.c +++ b/src/p2p/p2p_build.c @@ -802,7 +802,7 @@ int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, wpabuf_put_be16(buf, p2p->cfg->config_methods); } - if (wps_build_wfa_ext(buf, 0, NULL, 0) < 0) + if (wps_build_wfa_ext(buf, 0, NULL, 0, 0) < 0) return -1; if (all_attr && p2p->cfg->num_sec_dev_types) { diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c index 16c28a0d95ee7..aa18af6c16c6b 100644 --- a/src/p2p/p2p_group.c +++ b/src/p2p/p2p_group.c @@ -941,7 +941,8 @@ int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id, if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, m->addr, group->cfg->interface_addr, group->cfg->interface_addr, - wpabuf_head(req), wpabuf_len(req), 200) < 0) + wpabuf_head(req), wpabuf_len(req), 200, NULL) + < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 6a4d751c090ab..d2c55c9976c2e 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -707,7 +707,9 @@ void p2p_channels_dump(struct p2p_data *p2p, const char *title, int p2p_channel_select(struct p2p_channels *chans, const int *classes, u8 *op_class, u8 *op_channel); int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class, - u8 *op_channel); + u8 *op_channel, + struct wpa_freq_range_list *avoid_list, + struct wpa_freq_range_list *disallow_list); /* p2p_parse.c */ void p2p_copy_filter_devname(char *dst, size_t dst_len, diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c index bbba001a7c930..77d662a47ae24 100644 --- a/src/p2p/p2p_invitation.c +++ b/src/p2p/p2p_invitation.c @@ -488,7 +488,7 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, if (*msg.status == P2P_SC_FAIL_NO_COMMON_CHANNELS && p2p->retry_invite_req && p2p_channel_random_social(&p2p->cfg->channels, &p2p->op_reg_class, - &p2p->op_channel) == 0) { + &p2p->op_channel, NULL, NULL) == 0) { p2p->retry_invite_req = 0; p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p->cfg->stop_listen(p2p->cfg->cb_ctx); diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c index 2e2aa8ad06f08..1a62a44a2df39 100644 --- a/src/p2p/p2p_utils.c +++ b/src/p2p/p2p_utils.c @@ -413,17 +413,30 @@ int p2p_channel_select(struct p2p_channels *chans, const int *classes, int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class, - u8 *op_channel) + u8 *op_channel, + struct wpa_freq_range_list *avoid_list, + struct wpa_freq_range_list *disallow_list) { u8 chan[4]; unsigned int num_channels = 0; - /* Try to find available social channels from 2.4 GHz */ - if (p2p_channels_includes(chans, 81, 1)) + /* Try to find available social channels from 2.4 GHz. + * If the avoid_list includes any of the 2.4 GHz social channels, that + * channel is not allowed by p2p_channels_includes() rules. However, it + * is assumed to allow minimal traffic for P2P negotiation, so allow it + * here for social channel selection unless explicitly disallowed in the + * disallow_list. */ + if (p2p_channels_includes(chans, 81, 1) || + (freq_range_list_includes(avoid_list, 2412) && + !freq_range_list_includes(disallow_list, 2412))) chan[num_channels++] = 1; - if (p2p_channels_includes(chans, 81, 6)) + if (p2p_channels_includes(chans, 81, 6) || + (freq_range_list_includes(avoid_list, 2437) && + !freq_range_list_includes(disallow_list, 2437))) chan[num_channels++] = 6; - if (p2p_channels_includes(chans, 81, 11)) + if (p2p_channels_includes(chans, 81, 11) || + (freq_range_list_includes(avoid_list, 2462) && + !freq_range_list_includes(disallow_list, 2462))) chan[num_channels++] = 11; /* Try to find available social channels from 60 GHz */ diff --git a/src/pae/ieee802_1x_cp.c b/src/pae/ieee802_1x_cp.c index 360fcd3f5fcdf..1c4dc3e63c9ff 100644 --- a/src/pae/ieee802_1x_cp.c +++ b/src/pae/ieee802_1x_cp.c @@ -38,12 +38,10 @@ struct ieee802_1x_cp_sm { /* Logon -> CP */ enum connect_type connect; - u8 *authorization_data; /* KaY -> CP */ Boolean chgd_server; /* clear by CP */ Boolean elected_self; - u8 *authorization_data1; enum confidentiality_offset cipher_offset; u64 cipher_suite; Boolean new_sak; /* clear by CP */ @@ -216,6 +214,10 @@ SM_STATE(CP, RECEIVE) SM_ENTRY(CP, RECEIVE); /* RECEIVE state machine not keep with Figure 12-2 in * IEEE Std 802.1X-2010 */ + if (sm->oki) { + ieee802_1x_kay_delete_sas(sm->kay, sm->oki); + os_free(sm->oki); + } sm->oki = sm->lki; sm->oan = sm->lan; sm->otx = sm->ltx; @@ -320,8 +322,11 @@ SM_STATE(CP, RETIRE) SM_ENTRY(CP, RETIRE); /* RETIRE state machine not keep with Figure 12-2 in * IEEE Std 802.1X-2010 */ - os_free(sm->oki); - sm->oki = NULL; + if (sm->oki) { + ieee802_1x_kay_delete_sas(sm->kay, sm->oki); + os_free(sm->oki); + sm->oki = NULL; + } sm->orx = FALSE; sm->otx = FALSE; ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan, @@ -383,7 +388,8 @@ SM_STEP(CP) if (!sm->elected_self) SM_ENTER(CP, READY); if (sm->elected_self && - (sm->all_receiving || !sm->transmit_when)) + (sm->all_receiving || !sm->controlled_port_enabled || + !sm->transmit_when)) SM_ENTER(CP, TRANSMIT); break; @@ -406,8 +412,8 @@ SM_STEP(CP) case CP_READY: if (sm->new_sak || changed_connect(sm)) - SM_ENTER(CP, RECEIVE); - if (sm->server_transmitting) + SM_ENTER(CP, ABANDON); + if (sm->server_transmitting || !sm->controlled_port_enabled) SM_ENTER(CP, TRANSMIT); break; case CP_ABANDON: @@ -464,7 +470,6 @@ struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay) sm->retire_delay = MKA_SAK_RETIRE_TIME; sm->CP_state = CP_BEGIN; sm->changed = FALSE; - sm->authorization_data = NULL; wpa_printf(MSG_DEBUG, "CP: state machine created"); @@ -476,7 +481,6 @@ struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay) secy_cp_control_confidentiality_offset(sm->kay, sm->confidentiality_offset); - SM_ENTER(CP, INIT); SM_STEP_RUN(CP); return sm; @@ -518,7 +522,6 @@ void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm) eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL); os_free(sm->lki); os_free(sm->oki); - os_free(sm->authorization_data); os_free(sm); } @@ -589,19 +592,6 @@ void ieee802_1x_cp_set_electedself(void *cp_ctx, Boolean status) /** - * ieee802_1x_cp_set_authorizationdata - - */ -void ieee802_1x_cp_set_authorizationdata(void *cp_ctx, u8 *pdata, int len) -{ - struct ieee802_1x_cp_sm *sm = cp_ctx; - os_free(sm->authorization_data); - sm->authorization_data = os_zalloc(len); - if (sm->authorization_data) - os_memcpy(sm->authorization_data, pdata, len); -} - - -/** * ieee802_1x_cp_set_ciphersuite - */ void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, u64 cs) diff --git a/src/pae/ieee802_1x_cp.h b/src/pae/ieee802_1x_cp.h index 695629e5c0bce..a357b278f40a8 100644 --- a/src/pae/ieee802_1x_cp.h +++ b/src/pae/ieee802_1x_cp.h @@ -25,7 +25,6 @@ void ieee802_1x_cp_connect_authenticated(void *cp_ctx); void ieee802_1x_cp_connect_secure(void *cp_ctx); void ieee802_1x_cp_signal_chgdserver(void *cp_ctx); void ieee802_1x_cp_set_electedself(void *cp_ctx, Boolean status); -void ieee802_1x_cp_set_authorizationdata(void *cp_ctx, u8 *pdata, int len); void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, u64 cs); void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset); void ieee802_1x_cp_signal_newsak(void *cp_ctx); diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c index cda23fcab41aa..b4455c8f4e081 100644 --- a/src/pae/ieee802_1x_kay.c +++ b/src/pae/ieee802_1x_kay.c @@ -1,5 +1,5 @@ /* - * IEEE 802.1X-2010 Key Agree Protocol of PAE state machine + * IEEE 802.1X-2010 Key Agreement Protocol of PAE state machine * Copyright (c) 2013, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. @@ -27,6 +27,9 @@ #define DEFAULT_ICV_LEN 16 #define MAX_ICV_LEN 32 /* 32 bytes, 256 bits */ +#define MAX_MISSING_SAK_USE 10 /* Accept up to 10 inbound MKPDUs without + * SAK-USE before dropping */ + #define PENDING_PN_EXHAUSTION 0xC0000000 #define MKA_ALIGN_LENGTH(len) (((len) + 0x3) & ~0x3) @@ -43,7 +46,6 @@ static struct macsec_ciphersuite cipher_suite_tbl[] = { .name = CS_NAME_GCM_AES_128, .capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50, .sak_len = DEFAULT_SA_KEY_LEN, - .index = 0, }, /* GCM-AES-256 */ { @@ -51,7 +53,6 @@ static struct macsec_ciphersuite cipher_suite_tbl[] = { .name = CS_NAME_GCM_AES_256, .capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50, .sak_len = 32, - .index = 1 /* index */ }, }; #define CS_TABLE_SIZE (ARRAY_SIZE(cipher_suite_tbl)) @@ -61,19 +62,13 @@ static struct mka_alg mka_alg_tbl[] = { { .parameter = MKA_ALGO_AGILITY_2009, - /* 128-bit CAK, KEK, ICK, ICV */ - .cak_len = DEFAULT_ICV_LEN, - .kek_len = DEFAULT_ICV_LEN, - .ick_len = DEFAULT_ICV_LEN, .icv_len = DEFAULT_ICV_LEN, - .cak_trfm = ieee802_1x_cak_128bits_aes_cmac, - .ckn_trfm = ieee802_1x_ckn_128bits_aes_cmac, - .kek_trfm = ieee802_1x_kek_128bits_aes_cmac, - .ick_trfm = ieee802_1x_ick_128bits_aes_cmac, - .icv_hash = ieee802_1x_icv_128bits_aes_cmac, - - .index = 1, + .cak_trfm = ieee802_1x_cak_aes_cmac, + .ckn_trfm = ieee802_1x_ckn_aes_cmac, + .kek_trfm = ieee802_1x_kek_aes_cmac, + .ick_trfm = ieee802_1x_ick_aes_cmac, + .icv_hash = ieee802_1x_icv_aes_cmac, }, }; #define MKA_ALG_TABLE_SIZE (ARRAY_SIZE(mka_alg_tbl)) @@ -109,6 +104,34 @@ static u8 get_mka_param_body_type(const void *body) } +static const char * mi_txt(const u8 *mi) +{ + static char txt[MI_LEN * 2 + 1]; + + wpa_snprintf_hex(txt, sizeof(txt), mi, MI_LEN); + return txt; +} + + +static const char * sci_txt(const struct ieee802_1x_mka_sci *sci) +{ + static char txt[ETH_ALEN * 3 + 1 + 5 + 1]; + + os_snprintf(txt, sizeof(txt), MACSTR "@%u", + MAC2STR(sci->addr), be_to_host16(sci->port)); + return txt; +} + + +static const char * algo_agility_txt(const u8 *algo_agility) +{ + static char txt[4 * 2 + 1]; + + wpa_snprintf_hex(txt, sizeof(txt), algo_agility, 4); + return txt; +} + + /** * ieee802_1x_mka_dump_basic_body - */ @@ -120,26 +143,25 @@ ieee802_1x_mka_dump_basic_body(struct ieee802_1x_mka_basic_body *body) if (!body) return; + /* IEEE Std 802.1X-2010, Figure 11-8 */ body_len = get_mka_param_body_len(body); - wpa_printf(MSG_DEBUG, "*** MKA Basic Parameter set ***"); - wpa_printf(MSG_DEBUG, "\tVersion.......: %d", body->version); - wpa_printf(MSG_DEBUG, "\tPriority......: %d", body->priority); - wpa_printf(MSG_DEBUG, "\tKeySvr........: %d", body->key_server); - wpa_printf(MSG_DEBUG, "\tMACSecDesired.: %d", body->macsec_desired); - wpa_printf(MSG_DEBUG, "\tMACSecCapable.: %d", body->macsec_capability); - wpa_printf(MSG_DEBUG, "\tBody Length...: %zu", body_len); - wpa_printf(MSG_DEBUG, "\tSCI MAC.......: " MACSTR, - MAC2STR(body->actor_sci.addr)); - wpa_printf(MSG_DEBUG, "\tSCI Port .....: %d", - be_to_host16(body->actor_sci.port)); - wpa_hexdump(MSG_DEBUG, "\tMember Id.....:", - body->actor_mi, sizeof(body->actor_mi)); - wpa_printf(MSG_DEBUG, "\tMessage Number: %d", + wpa_printf(MSG_DEBUG, "MKA Basic Parameter Set"); + wpa_printf(MSG_DEBUG, "\tMKA Version Identifier: %d", body->version); + wpa_printf(MSG_DEBUG, "\tKey Server Priority: %d", body->priority); + wpa_printf(MSG_DEBUG, "\tKey Server: %d", body->key_server); + wpa_printf(MSG_DEBUG, "\tMACsec Desired: %d", body->macsec_desired); + wpa_printf(MSG_DEBUG, "\tMACsec Capability: %d", + body->macsec_capability); + wpa_printf(MSG_DEBUG, "\tParameter set body length: %zu", body_len); + wpa_printf(MSG_DEBUG, "\tSCI: %s", sci_txt(&body->actor_sci)); + wpa_printf(MSG_DEBUG, "\tActor's Member Identifier: %s", + mi_txt(body->actor_mi)); + wpa_printf(MSG_DEBUG, "\tActor's Message Number: %d", be_to_host32(body->actor_mn)); - wpa_hexdump(MSG_DEBUG, "\tAlgo Agility..:", - body->algo_agility, sizeof(body->algo_agility)); - wpa_hexdump_ascii(MSG_DEBUG, "\tCAK Name......:", body->ckn, - body_len + MKA_HDR_LEN - sizeof(*body)); + wpa_printf(MSG_DEBUG, "\tAlgorithm Agility: %s", + algo_agility_txt(body->algo_agility)); + wpa_hexdump(MSG_DEBUG, "\tCAK Name", body->ckn, + body_len + MKA_HDR_LEN - sizeof(*body)); } @@ -157,20 +179,21 @@ ieee802_1x_mka_dump_peer_body(struct ieee802_1x_mka_peer_body *body) if (body == NULL) return; + /* IEEE Std 802.1X-2010, Figure 11-9 */ body_len = get_mka_param_body_len(body); if (body->type == MKA_LIVE_PEER_LIST) { - wpa_printf(MSG_DEBUG, "*** Live Peer List ***"); - wpa_printf(MSG_DEBUG, "\tBody Length...: %zu", body_len); + wpa_printf(MSG_DEBUG, "Live Peer List parameter set"); + wpa_printf(MSG_DEBUG, "\tBody Length: %zu", body_len); } else if (body->type == MKA_POTENTIAL_PEER_LIST) { - wpa_printf(MSG_DEBUG, "*** Potential Live Peer List ***"); - wpa_printf(MSG_DEBUG, "\tBody Length...: %zu", body_len); + wpa_printf(MSG_DEBUG, "Potential Peer List parameter set"); + wpa_printf(MSG_DEBUG, "\tBody Length: %zu", body_len); } for (i = 0; i < body_len; i += MI_LEN + sizeof(mn)) { mi = body->peer + i; os_memcpy(&mn, mi + MI_LEN, sizeof(mn)); - wpa_hexdump_ascii(MSG_DEBUG, "\tMember Id.....:", mi, MI_LEN); - wpa_printf(MSG_DEBUG, "\tMessage Number: %d", be_to_host32(mn)); + wpa_printf(MSG_DEBUG, "\tMember Id: %s Message Number: %d", + mi_txt(mi), be_to_host32(mn)); } } @@ -186,18 +209,20 @@ ieee802_1x_mka_dump_dist_sak_body(struct ieee802_1x_mka_dist_sak_body *body) if (body == NULL) return; + /* IEEE Std 802.1X-2010, Figure 11-11 and 11-12 */ body_len = get_mka_param_body_len(body); - wpa_printf(MSG_INFO, "*** Distributed SAK ***"); - wpa_printf(MSG_INFO, "\tDistributed AN........: %d", body->dan); - wpa_printf(MSG_INFO, "\tConfidentiality Offset: %d", + wpa_printf(MSG_DEBUG, "Distributed SAK parameter set"); + wpa_printf(MSG_DEBUG, "\tDistributed AN........: %d", body->dan); + wpa_printf(MSG_DEBUG, "\tConfidentiality Offset: %d", body->confid_offset); - wpa_printf(MSG_INFO, "\tBody Length...........: %zu", body_len); + wpa_printf(MSG_DEBUG, "\tBody Length...........: %zu", body_len); if (!body_len) return; - wpa_printf(MSG_INFO, "\tKey Number............: %d", + wpa_printf(MSG_DEBUG, "\tKey Number............: %d", be_to_host32(body->kn)); - wpa_hexdump(MSG_INFO, "\tAES Key Wrap of SAK...:", body->sak, 24); + /* TODO: Other than GCM-AES-128 case: MACsec Cipher Suite */ + wpa_hexdump(MSG_DEBUG, "\tAES Key Wrap of SAK...:", body->sak, 24); } @@ -218,33 +243,32 @@ ieee802_1x_mka_dump_sak_use_body(struct ieee802_1x_mka_sak_use_body *body) if (body == NULL) return; + /* IEEE Std 802.1X-2010, Figure 11-10 */ body_len = get_mka_param_body_len(body); - wpa_printf(MSG_DEBUG, "*** MACsec SAK Use ***"); + wpa_printf(MSG_DEBUG, "MACsec SAK Use parameter set"); wpa_printf(MSG_DEBUG, "\tLatest Key AN....: %d", body->lan); wpa_printf(MSG_DEBUG, "\tLatest Key Tx....: %s", yes_no(body->ltx)); wpa_printf(MSG_DEBUG, "\tLatest Key Rx....: %s", yes_no(body->lrx)); - wpa_printf(MSG_DEBUG, "\tOld Key AN....: %d", body->oan); - wpa_printf(MSG_DEBUG, "\tOld Key Tx....: %s", yes_no(body->otx)); - wpa_printf(MSG_DEBUG, "\tOld Key Rx....: %s", yes_no(body->orx)); - wpa_printf(MSG_DEBUG, "\tPlain Key Tx....: %s", yes_no(body->ptx)); - wpa_printf(MSG_DEBUG, "\tPlain Key Rx....: %s", yes_no(body->prx)); + wpa_printf(MSG_DEBUG, "\tOld Key AN.......: %d", body->oan); + wpa_printf(MSG_DEBUG, "\tOld Key Tx.......: %s", yes_no(body->otx)); + wpa_printf(MSG_DEBUG, "\tOld Key Rx.......: %s", yes_no(body->orx)); + wpa_printf(MSG_DEBUG, "\tPlain Tx.........: %s", yes_no(body->ptx)); + wpa_printf(MSG_DEBUG, "\tPlain Rx.........: %s", yes_no(body->prx)); wpa_printf(MSG_DEBUG, "\tDelay Protect....: %s", yes_no(body->delay_protect)); wpa_printf(MSG_DEBUG, "\tBody Length......: %d", body_len); if (!body_len) return; - wpa_hexdump(MSG_DEBUG, "\tKey Server MI....:", - body->lsrv_mi, sizeof(body->lsrv_mi)); + wpa_printf(MSG_DEBUG, "\tKey Server MI....: %s", mi_txt(body->lsrv_mi)); wpa_printf(MSG_DEBUG, "\tKey Number.......: %u", be_to_host32(body->lkn)); wpa_printf(MSG_DEBUG, "\tLowest PN........: %u", be_to_host32(body->llpn)); - wpa_hexdump_ascii(MSG_DEBUG, "\tOld Key Server MI....:", - body->osrv_mi, sizeof(body->osrv_mi)); - wpa_printf(MSG_DEBUG, "\tOld Key Number.......: %u", + wpa_printf(MSG_DEBUG, "\tOld Key Server MI: %s", mi_txt(body->osrv_mi)); + wpa_printf(MSG_DEBUG, "\tOld Key Number...: %u", be_to_host32(body->okn)); - wpa_printf(MSG_DEBUG, "\tOld Lowest PN........: %u", + wpa_printf(MSG_DEBUG, "\tOld Lowest PN....: %u", be_to_host32(body->olpn)); } @@ -371,7 +395,7 @@ ieee802_1x_kay_get_peer(struct ieee802_1x_mka_participant *participant, */ static struct macsec_ciphersuite * ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant, - const u8 *cs_id) + const u8 *cs_id, unsigned int *idx) { unsigned int i; u64 cs; @@ -381,8 +405,10 @@ ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant, cs = be_to_host64(_cs); for (i = 0; i < CS_TABLE_SIZE; i++) { - if (cipher_suite_tbl[i].id == cs) + if (cipher_suite_tbl[i].id == cs) { + *idx = i; return &cipher_suite_tbl[i]; + } } return NULL; @@ -464,7 +490,7 @@ ieee802_1x_kay_init_receive_sa(struct receive_sc *psc, u8 an, u32 lowest_pn, dl_list_add(&psc->sa_list, &psa->list); wpa_printf(MSG_DEBUG, - "KaY: Create receive SA(AN: %hhu lowest_pn: %u of SC", + "KaY: Create receive SA(an: %hhu lowest_pn: %u) of SC", an, lowest_pn); return psa; @@ -511,8 +537,8 @@ ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci) psc->receiving = FALSE; dl_list_init(&psc->sa_list); - wpa_printf(MSG_DEBUG, "KaY: Create receive SC"); - wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)psci, sizeof(*psci)); + wpa_printf(MSG_DEBUG, "KaY: Create receive SC: SCI %s", + sci_txt(&psc->sci)); return psc; } @@ -549,10 +575,8 @@ ieee802_1x_kay_deinit_receive_sc( static void ieee802_1x_kay_dump_peer(struct ieee802_1x_kay_peer *peer) { - wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi)); - wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn); - wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN); - wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port); + wpa_printf(MSG_DEBUG, "\tMI: %s MN: %d SCI: %s", + mi_txt(peer->mi), peer->mn, sci_txt(&peer->sci)); } @@ -571,6 +595,7 @@ ieee802_1x_kay_create_peer(const u8 *mi, u32 mn) peer->mn = mn; peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; peer->sak_used = FALSE; + peer->missing_sak_use_count = 0; return peer; } @@ -599,9 +624,13 @@ ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant, return NULL; } + if (secy_create_receive_sc(participant->kay, rxsc)) { + os_free(rxsc); + os_free(peer); + return NULL; + } dl_list_add(&participant->live_peers, &peer->list); dl_list_add(&participant->rxsc_list, &rxsc->list); - secy_create_receive_sc(participant->kay, rxsc); wpa_printf(MSG_DEBUG, "KaY: Live peer created"); ieee802_1x_kay_dump_peer(peer); @@ -625,7 +654,7 @@ ieee802_1x_kay_create_potential_peer( dl_list_add(&participant->potential_peers, &peer->list); - wpa_printf(MSG_DEBUG, "KaY: potential peer created"); + wpa_printf(MSG_DEBUG, "KaY: Potential peer created"); ieee802_1x_kay_dump_peer(peer); return peer; @@ -655,14 +684,19 @@ ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant, peer->mn = mn; peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; - wpa_printf(MSG_DEBUG, "KaY: move potential peer to live peer"); + wpa_printf(MSG_DEBUG, "KaY: Move potential peer to live peer"); ieee802_1x_kay_dump_peer(peer); dl_list_del(&peer->list); + if (secy_create_receive_sc(participant->kay, rxsc)) { + wpa_printf(MSG_ERROR, "KaY: Can't create SC, discard peer"); + os_free(rxsc); + os_free(peer); + return NULL; + } dl_list_add_tail(&participant->live_peers, &peer->list); dl_list_add(&participant->rxsc_list, &rxsc->list); - secy_create_receive_sc(participant->kay, rxsc); return peer; } @@ -704,12 +738,15 @@ ieee802_1x_mka_encode_basic_body( { struct ieee802_1x_mka_basic_body *body; struct ieee802_1x_kay *kay = participant->kay; - unsigned int length = ieee802_1x_mka_basic_body_length(participant); + unsigned int length = sizeof(struct ieee802_1x_mka_basic_body); - body = wpabuf_put(buf, length); + length += participant->ckn.len; + body = wpabuf_put(buf, MKA_ALIGN_LENGTH(length)); body->version = kay->mka_version; body->priority = kay->actor_priority; + /* The Key Server flag is set if and only if the participant has not + * decided that another participant is or will be the Key Server. */ if (participant->is_elected) body->key_server = participant->is_key_server; else @@ -765,11 +802,11 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, if (body->version > MKA_VERSION_ID) { wpa_printf(MSG_DEBUG, - "KaY: peer's version(%d) greater than mka current version(%d)", + "KaY: Peer's version(%d) greater than MKA current version(%d)", body->version, MKA_VERSION_ID); } if (kay->is_obliged_key_server && body->key_server) { - wpa_printf(MSG_DEBUG, "I must be as key server"); + wpa_printf(MSG_DEBUG, "KaY: I must be key server - ignore MKPDU claiming to be from a key server"); return NULL; } @@ -783,7 +820,8 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, (sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN); participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len); if (!participant) { - wpa_printf(MSG_DEBUG, "Peer is not included in my CA"); + wpa_printf(MSG_DEBUG, + "KaY: Peer is not included in my CA - ignore MKPDU"); return NULL; } @@ -791,6 +829,9 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, if (os_memcmp(body->actor_mi, participant->mi, MI_LEN) == 0) { if (!reset_participant_mi(participant)) return NULL; + wpa_printf(MSG_DEBUG, + "KaY: Peer using my MI - selected a new random MI: %s", + mi_txt(participant->mi)); } os_memcpy(participant->current_peer_id.mi, body->actor_mi, MI_LEN); @@ -802,24 +843,48 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, /* handler peer */ peer = ieee802_1x_kay_get_peer(participant, body->actor_mi); if (!peer) { - /* Check duplicated SCI */ - /* TODO: What policy should be applied to detect duplicated SCI - * is active attacker or a valid peer whose MI is be changed? + /* Check duplicated SCI + * + * A duplicated SCI indicates either an active attacker or + * a valid peer whose MI is being changed. The latter scenario + * is more likely because to have gotten this far the received + * MKPDU must have had a valid ICV, indicating the peer holds + * the same CAK as our participant. + * + * Before creating a new peer object for the new MI we must + * clean up the resources (SCs and SAs) associated with the + * old peer. An easy way to do this is to ignore MKPDUs with + * the new MI's for now and just wait for the old peer to + * time out and clean itself up (within MKA_LIFE_TIME). + * + * This method is preferable to deleting the old peer here + * and now and continuing on with processing because if this + * MKPDU is from an attacker it's better to ignore the MKPDU + * than to process it (and delete a valid peer as well). */ peer = ieee802_1x_kay_get_peer_sci(participant, &body->actor_sci); if (peer) { + time_t new_expire; + wpa_printf(MSG_WARNING, - "KaY: duplicated SCI detected, Maybe active attacker"); - dl_list_del(&peer->list); - os_free(peer); + "KaY: duplicated SCI detected - maybe active attacker or peer selected new MI - ignore MKPDU"); + /* Reduce timeout to speed up this process but left the + * chance for old one to prove aliveness. */ + new_expire = time(NULL) + MKA_HELLO_TIME * 1.5 / 1000; + if (peer->expire > new_expire) + peer->expire = new_expire; + return NULL; } peer = ieee802_1x_kay_create_potential_peer( participant, body->actor_mi, be_to_host32(body->actor_mn)); - if (!peer) + if (!peer) { + wpa_printf(MSG_DEBUG, + "KaY: No potential peer entry found - ignore MKPDU"); return NULL; + } peer->macsec_desired = body->macsec_desired; peer->macsec_capability = body->macsec_capability; @@ -827,13 +892,13 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, peer->key_server_priority = body->priority; } else if (peer->mn < be_to_host32(body->actor_mn)) { peer->mn = be_to_host32(body->actor_mn); - peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; peer->macsec_desired = body->macsec_desired; peer->macsec_capability = body->macsec_capability; peer->is_key_server = (Boolean) body->key_server; peer->key_server_priority = body->priority; } else { - wpa_printf(MSG_WARNING, "KaY: The peer MN have received"); + wpa_printf(MSG_WARNING, + "KaY: The peer MN did not increase - ignore MKPDU"); return NULL; } @@ -978,8 +1043,8 @@ ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant, for (pos = mka_msg, left_len = msg_len; left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN; - left_len -= body_len + MKA_HDR_LEN, - pos += body_len + MKA_HDR_LEN) { + left_len -= MKA_ALIGN_LENGTH(body_len) + MKA_HDR_LEN, + pos += MKA_ALIGN_LENGTH(body_len) + MKA_HDR_LEN) { hdr = (struct ieee802_1x_mka_hdr *) pos; body_len = get_mka_param_body_len(hdr); body_type = get_mka_param_body_type(hdr); @@ -1014,9 +1079,15 @@ ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant, peer_mi = (const struct ieee802_1x_mka_peer_id *) (pos + MKA_HDR_LEN + i); if (os_memcmp(peer_mi->mi, participant->mi, - MI_LEN) == 0 && - be_to_host32(peer_mi->mn) == participant->mn) - return TRUE; + MI_LEN) == 0) { + u32 mn = be_to_host32(peer_mi->mn); + + wpa_printf(MSG_DEBUG, + "KaY: My MI - received MN %u, most recently transmitted MN %u", + mn, participant->mn); + if (mn == participant->mn) + return TRUE; + } } } @@ -1072,7 +1143,6 @@ static int ieee802_1x_mka_decode_live_peer_body( peer = ieee802_1x_kay_get_peer(participant, peer_mi->mi); if (peer) { peer->mn = peer_mn; - peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; } else if (!ieee802_1x_kay_create_potential_peer( participant, peer_mi->mi, peer_mn)) { return -1; @@ -1154,27 +1224,38 @@ ieee802_1x_mka_get_sak_use_length( /** - * + * ieee802_1x_mka_get_lpn */ static u32 ieee802_1x_mka_get_lpn(struct ieee802_1x_mka_participant *principal, struct ieee802_1x_mka_ki *ki) { - struct receive_sa *rxsa; - struct receive_sc *rxsc; + struct transmit_sa *txsa; u32 lpn = 0; - dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) { - dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list) - { - if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) { - secy_get_receive_lowest_pn(principal->kay, - rxsa); - - lpn = lpn > rxsa->lowest_pn ? - lpn : rxsa->lowest_pn; - break; - } + dl_list_for_each(txsa, &principal->txsc->sa_list, + struct transmit_sa, list) { + if (is_ki_equal(&txsa->pkey->key_identifier, ki)) { + /* Per IEEE Std 802.1X-2010, Clause 9, "Each SecY uses + * MKA to communicate the lowest PN used for + * transmission with the SAK within the last two + * seconds". Achieve this 2 second delay by setting the + * lpn using the transmit next PN (i.e., txsa->next_pn) + * that was read last time here (i.e., mka_hello_time + * 2 seconds ago). + * + * The lowest acceptable PN is the same as the last + * transmitted PN, which is one less than the next + * transmit PN. + * + * NOTE: This method only works if mka_hello_time is 2s. + */ + lpn = (txsa->next_pn > 0) ? (txsa->next_pn - 1) : 0; + + /* Now read the current transmit next PN for use next + * time through. */ + secy_get_transmit_next_pn(principal->kay, txsa); + break; } } @@ -1214,8 +1295,9 @@ ieee802_1x_mka_encode_sak_use_body( return 0; } - /* data protect, lowest accept packet number */ - body->delay_protect = kay->macsec_replay_protect; + /* data delay protect */ + body->delay_protect = kay->mka_hello_time <= MKA_BOUNDED_HELLO_TIME; + /* lowest accept packet number */ pn = ieee802_1x_mka_get_lpn(participant, &participant->lki); if (pn > kay->pn_exhaustion) { wpa_printf(MSG_WARNING, "KaY: My LPN exhaustion"); @@ -1276,7 +1358,8 @@ ieee802_1x_mka_decode_sak_use_body( struct ieee802_1x_mka_hdr *hdr; struct ieee802_1x_mka_sak_use_body *body; struct ieee802_1x_kay_peer *peer; - struct transmit_sa *txsa; + struct receive_sc *rxsc; + struct receive_sa *rxsa; struct data_key *sa_key = NULL; size_t body_len; struct ieee802_1x_mka_ki ki; @@ -1292,7 +1375,9 @@ ieee802_1x_mka_decode_sak_use_body( peer = ieee802_1x_kay_get_live_peer(participant, participant->current_peer_id.mi); if (!peer) { - wpa_printf(MSG_WARNING, "KaY: the peer is not my live peer"); + wpa_printf(MSG_WARNING, + "KaY: The peer (%s) is not my live peer - ignore MACsec SAK Use parameter set", + mi_txt(participant->current_peer_id.mi)); return -1; } @@ -1336,7 +1421,7 @@ ieee802_1x_mka_decode_sak_use_body( } } if (!found) { - wpa_printf(MSG_WARNING, "KaY: Latest key is invalid"); + wpa_printf(MSG_INFO, "KaY: Latest key is invalid"); return -1; } if (os_memcmp(participant->lki.mi, body->lsrv_mi, @@ -1366,7 +1451,7 @@ ieee802_1x_mka_decode_sak_use_body( if (body->delay_protect && (!be_to_host32(body->llpn) || !be_to_host32(body->olpn))) { wpa_printf(MSG_WARNING, - "KaY: Lowest packet number should greater than 0 when delay_protect is TRUE"); + "KaY: Lowest packet number should be greater than 0 when delay_protect is TRUE"); return -1; } @@ -1385,7 +1470,7 @@ ieee802_1x_mka_decode_sak_use_body( ieee802_1x_cp_sm_step(kay->cp); } - /* if i'm key server, and detects peer member pn exhaustion, rekey.*/ + /* if I'm key server, and detects peer member pn exhaustion, rekey. */ lpn = be_to_host32(body->llpn); if (lpn > kay->pn_exhaustion) { if (participant->is_key_server) { @@ -1394,26 +1479,41 @@ ieee802_1x_mka_decode_sak_use_body( } } + if (sa_key) + sa_key->next_pn = lpn; found = FALSE; - dl_list_for_each(txsa, &participant->txsc->sa_list, - struct transmit_sa, list) { - if (sa_key != NULL && txsa->pkey == sa_key) { - found = TRUE; - break; + dl_list_for_each(rxsc, &participant->rxsc_list, struct receive_sc, + list) { + dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, + list) { + if (sa_key && rxsa->pkey == sa_key) { + found = TRUE; + break; + } } + if (found) + break; } if (!found) { - wpa_printf(MSG_WARNING, "KaY: Can't find txsa"); + wpa_printf(MSG_WARNING, "KaY: Can't find rxsa"); return -1; } - /* FIXME: Secy creates txsa with default npn. If MKA detected Latest Key - * npn is larger than txsa's npn, set it to txsa. - */ - secy_get_transmit_next_pn(kay, txsa); - if (lpn > txsa->next_pn) { - secy_set_transmit_next_pn(kay, txsa); - wpa_printf(MSG_INFO, "KaY: update lpn =0x%x", lpn); + if (body->delay_protect) { + secy_get_receive_lowest_pn(participant->kay, rxsa); + if (lpn > rxsa->lowest_pn) { + /* Delay protect window (communicated via MKA) is + * tighter than SecY's current replay protect window, + * so tell SecY the new (and higher) lpn. */ + rxsa->lowest_pn = lpn; + secy_set_receive_lowest_pn(participant->kay, rxsa); + wpa_printf(MSG_DEBUG, "KaY: update lpn =0x%x", lpn); + } + /* FIX: Delay protection for olpn not implemented. + * Note that Old Key is only active for MKA_SAK_RETIRE_TIME + * (3 seconds) and delay protection does allow PN's within + * a 2 seconds window, so olpn would be a lot of work for + * just 1 second's worth of protection. */ } return 0; @@ -1427,7 +1527,8 @@ static Boolean ieee802_1x_mka_dist_sak_body_present( struct ieee802_1x_mka_participant *participant) { - return participant->to_dist_sak && participant->new_key; + return participant->is_key_server && participant->to_dist_sak && + participant->new_key; } @@ -1478,6 +1579,11 @@ ieee802_1x_mka_encode_dist_sak_body( } sak = participant->new_key; + if (!sak) { + wpa_printf(MSG_DEBUG, + "KaY: No SAK available to build Distributed SAK parameter set"); + return -1; + } body->confid_offset = sak->confidentiality_offset; body->dan = sak->an; body->kn = host_to_be32(sak->key_identifier.kn); @@ -1492,7 +1598,7 @@ ieee802_1x_mka_encode_dist_sak_body( os_memcpy(body->sak, &cs, CS_ID_LEN); sak_pos = CS_ID_LEN; } - if (aes_wrap(participant->kek.key, 16, + if (aes_wrap(participant->kek.key, participant->kek.len, cipher_suite_tbl[cs_index].sak_len / 8, sak->key, body->sak + sak_pos)) { wpa_printf(MSG_ERROR, "KaY: AES wrap failed"); @@ -1514,6 +1620,7 @@ static void ieee802_1x_kay_init_data_key(struct data_key *pkey) pkey->receives = TRUE; os_get_time(&pkey->created_time); + pkey->next_pn = 1; pkey->user = 1; } @@ -1553,7 +1660,7 @@ ieee802_1x_mka_decode_dist_sak_body( } if (participant->is_key_server) { wpa_printf(MSG_ERROR, - "KaY: I can't accept the distributed SAK as myself is key server "); + "KaY: Reject distributed SAK since I'm a key server"); return -1; } if (!kay->macsec_desired || @@ -1582,7 +1689,7 @@ ieee802_1x_mka_decode_dist_sak_body( participant->advised_desired = FALSE; ieee802_1x_cp_connect_authenticated(kay->cp); ieee802_1x_cp_sm_step(kay->cp); - wpa_printf(MSG_WARNING, "KaY:The Key server advise no MACsec"); + wpa_printf(MSG_WARNING, "KaY: The Key server advise no MACsec"); participant->to_use_sak = FALSE; return 0; } @@ -1601,7 +1708,8 @@ ieee802_1x_mka_decode_dist_sak_body( if (os_memcmp(sa_key->key_identifier.mi, participant->current_peer_id.mi, MI_LEN) == 0 && sa_key->key_identifier.kn == be_to_host32(body->kn)) { - wpa_printf(MSG_WARNING, "KaY:The Key has installed"); + wpa_printf(MSG_DEBUG, + "KaY: SAK has already been installed - do not set it again"); return 0; } } @@ -1612,7 +1720,10 @@ ieee802_1x_mka_decode_dist_sak_body( kay->macsec_csindex = DEFAULT_CS_INDEX; cs = &cipher_suite_tbl[kay->macsec_csindex]; } else { - cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak); + unsigned int idx; + + cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak, + &idx); if (!cs) { wpa_printf(MSG_ERROR, "KaY: I can't support the Cipher Suite advised by key server"); @@ -1620,7 +1731,7 @@ ieee802_1x_mka_decode_dist_sak_body( } sak_len = cs->sak_len; wrap_sak = body->sak + CS_ID_LEN; - kay->macsec_csindex = cs->index; + kay->macsec_csindex = idx; } unwrap_sak = os_zalloc(sak_len); @@ -1628,13 +1739,13 @@ ieee802_1x_mka_decode_dist_sak_body( wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); return -1; } - if (aes_unwrap(participant->kek.key, 16, sak_len >> 3, wrap_sak, - unwrap_sak)) { + if (aes_unwrap(participant->kek.key, participant->kek.len, + sak_len >> 3, wrap_sak, unwrap_sak)) { wpa_printf(MSG_ERROR, "KaY: AES unwrap failed"); os_free(unwrap_sak); return -1; } - wpa_hexdump_key(MSG_DEBUG, "\tAES Key Unwrap of SAK:", + wpa_hexdump_key(MSG_DEBUG, "\tAES Key Unwrap of SAK.:", unwrap_sak, sak_len); sa_key = os_zalloc(sizeof(*sa_key)); @@ -1691,7 +1802,12 @@ ieee802_1x_mka_get_icv_length(struct ieee802_1x_mka_participant *participant) { int length; - length = sizeof(struct ieee802_1x_mka_icv_body); + /* Determine if we need space for the ICV Indicator */ + if (mka_alg_tbl[participant->kay->mka_algindex].icv_len != + DEFAULT_ICV_LEN) + length = sizeof(struct ieee802_1x_mka_icv_body); + else + length = 0; length += mka_alg_tbl[participant->kay->mka_algindex].icv_len; return MKA_ALIGN_LENGTH(length); @@ -1710,20 +1826,23 @@ ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant, u8 cmac[MAX_ICV_LEN]; length = ieee802_1x_mka_get_icv_length(participant); - if (length != DEFAULT_ICV_LEN) { + if (mka_alg_tbl[participant->kay->mka_algindex].icv_len != + DEFAULT_ICV_LEN) { + wpa_printf(MSG_DEBUG, "KaY: ICV Indicator"); body = wpabuf_put(buf, MKA_HDR_LEN); body->type = MKA_ICV_INDICATOR; - set_mka_param_body_len(body, length - MKA_HDR_LEN); + length -= MKA_HDR_LEN; + set_mka_param_body_len(body, length); } if (mka_alg_tbl[participant->kay->mka_algindex].icv_hash( - participant->ick.key, wpabuf_head(buf), buf->used, cmac)) { - wpa_printf(MSG_ERROR, "KaY, omac1_aes_128 failed"); + participant->ick.key, participant->ick.len, + wpabuf_head(buf), wpabuf_len(buf), cmac)) { + wpa_printf(MSG_ERROR, "KaY: failed to calculate ICV"); return -1; } + wpa_hexdump(MSG_DEBUG, "KaY: ICV", cmac, length); - if (length != DEFAULT_ICV_LEN) - length -= MKA_HDR_LEN; os_memcpy(wpabuf_put(buf, length), cmac, length); return 0; @@ -1732,12 +1851,12 @@ ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant, /** * ieee802_1x_mka_decode_icv_body - */ -static u8 * +static const u8 * ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant, const u8 *mka_msg, size_t msg_len) { - struct ieee802_1x_mka_hdr *hdr; - struct ieee802_1x_mka_icv_body *body; + const struct ieee802_1x_mka_hdr *hdr; + const struct ieee802_1x_mka_icv_body *body; size_t body_len; size_t left_len; u8 body_type; @@ -1745,12 +1864,12 @@ ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant, pos = mka_msg; left_len = msg_len; - while (left_len > (MKA_HDR_LEN + DEFAULT_ICV_LEN)) { - hdr = (struct ieee802_1x_mka_hdr *) pos; - body_len = get_mka_param_body_len(hdr); + while (left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN) { + hdr = (const struct ieee802_1x_mka_hdr *) pos; + body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr)); body_type = get_mka_param_body_type(hdr); - if (left_len < (body_len + MKA_HDR_LEN)) + if (left_len < body_len + MKA_HDR_LEN) break; if (body_type != MKA_ICV_INDICATOR) { @@ -1759,16 +1878,15 @@ ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant, continue; } - body = (struct ieee802_1x_mka_icv_body *)pos; + body = (const struct ieee802_1x_mka_icv_body *) pos; if (body_len - < mka_alg_tbl[participant->kay->mka_algindex].icv_len) { + < mka_alg_tbl[participant->kay->mka_algindex].icv_len) return NULL; - } return body->icv; } - return (u8 *) (mka_msg + msg_len - DEFAULT_ICV_LEN); + return mka_msg + msg_len - DEFAULT_ICV_LEN; } @@ -1787,7 +1905,7 @@ ieee802_1x_mka_decode_dist_cak_body( body_len = get_mka_param_body_len(hdr); if (body_len < 28) { wpa_printf(MSG_ERROR, - "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 28 or more octets", + "KaY: MKA Use CAK Packet Body Length (%zu bytes) should be 28 or more octets", body_len); return -1; } @@ -1811,7 +1929,7 @@ ieee802_1x_mka_decode_kmd_body( body_len = get_mka_param_body_len(hdr); if (body_len < 5) { wpa_printf(MSG_ERROR, - "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 5 or more octets", + "KaY: MKA Use KMD Packet Body Length (%zu bytes) should be 5 or more octets", body_len); return -1; } @@ -1842,7 +1960,7 @@ struct mka_param_body_handler { static struct mka_param_body_handler mka_body_handler[] = { - /* basic parameter set */ + /* Basic parameter set */ { .body_tx = ieee802_1x_mka_encode_basic_body, .body_rx = NULL, @@ -1850,7 +1968,7 @@ static struct mka_param_body_handler mka_body_handler[] = { .body_present = ieee802_1x_mka_basic_body_present }, - /* live peer list parameter set */ + /* Live Peer List parameter set */ { .body_tx = ieee802_1x_mka_encode_live_peer_body, .body_rx = ieee802_1x_mka_decode_live_peer_body, @@ -1858,7 +1976,7 @@ static struct mka_param_body_handler mka_body_handler[] = { .body_present = ieee802_1x_mka_live_peer_body_present }, - /* potential peer list parameter set */ + /* Potential Peer List parameter set */ { .body_tx = ieee802_1x_mka_encode_potential_peer_body, .body_rx = ieee802_1x_mka_decode_potential_peer_body, @@ -1866,7 +1984,7 @@ static struct mka_param_body_handler mka_body_handler[] = { .body_present = ieee802_1x_mka_potential_peer_body_present }, - /* sak use parameter set */ + /* MACsec SAK Use parameter set */ { .body_tx = ieee802_1x_mka_encode_sak_use_body, .body_rx = ieee802_1x_mka_decode_sak_use_body, @@ -1874,7 +1992,7 @@ static struct mka_param_body_handler mka_body_handler[] = { .body_present = ieee802_1x_mka_sak_use_body_present }, - /* distribute sak parameter set */ + /* Distributed SAK parameter set */ { .body_tx = ieee802_1x_mka_encode_dist_sak_body, .body_rx = ieee802_1x_mka_decode_dist_sak_body, @@ -1882,7 +2000,7 @@ static struct mka_param_body_handler mka_body_handler[] = { .body_present = ieee802_1x_mka_dist_sak_body_present }, - /* distribute cak parameter set */ + /* Distribute CAK parameter set */ { .body_tx = NULL, .body_rx = ieee802_1x_mka_decode_dist_cak_body, @@ -1890,7 +2008,7 @@ static struct mka_param_body_handler mka_body_handler[] = { .body_present = NULL }, - /* kmd parameter set */ + /* KMD parameter set */ { .body_tx = NULL, .body_rx = ieee802_1x_mka_decode_kmd_body, @@ -1898,7 +2016,7 @@ static struct mka_param_body_handler mka_body_handler[] = { .body_present = NULL }, - /* announce parameter set */ + /* Announcement parameter set */ { .body_tx = NULL, .body_rx = ieee802_1x_mka_decode_announce_body, @@ -1906,7 +2024,7 @@ static struct mka_param_body_handler mka_body_handler[] = { .body_present = NULL }, - /* icv parameter set */ + /* ICV Indicator parameter set */ { .body_tx = ieee802_1x_mka_encode_icv_body, .body_rx = NULL, @@ -1965,7 +2083,7 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) */ if (dl_list_empty(&participant->live_peers)) { wpa_printf(MSG_ERROR, - "KaY: Live peers list must not empty when generating fresh SAK"); + "KaY: Live peers list must not be empty when generating fresh SAK"); return -1; } @@ -1979,7 +2097,7 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) */ if ((time(NULL) - kay->dist_time) < MKA_LIFE_TIME / 1000) { wpa_printf(MSG_ERROR, - "KaY: Life time have not elapsed since prior SAK distributed"); + "KaY: Life time has not elapsed since prior SAK distributed"); return -1; } @@ -2016,14 +2134,17 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) ctx_offset += sizeof(participant->mi); os_memcpy(context + ctx_offset, &kay->dist_kn, sizeof(kay->dist_kn)); - if (key_len == 16) { - ieee802_1x_sak_128bits_aes_cmac(participant->cak.key, - context, ctx_len, key); - } else if (key_len == 32) { - ieee802_1x_sak_128bits_aes_cmac(participant->cak.key, - context, ctx_len, key); + if (key_len == 16 || key_len == 32) { + if (ieee802_1x_sak_aes_cmac(participant->cak.key, + participant->cak.len, + context, ctx_len, + key, key_len)) { + wpa_printf(MSG_ERROR, "KaY: Failed to generate SAK"); + goto fail; + } } else { - wpa_printf(MSG_ERROR, "KaY: SAK Length not support"); + wpa_printf(MSG_ERROR, "KaY: SAK Length(%u) not supported", + key_len); goto fail; } wpa_hexdump_key(MSG_DEBUG, "KaY: generated new SAK", key, key_len); @@ -2155,7 +2276,7 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) participant->is_key_server = TRUE; participant->principal = TRUE; participant->new_sak = TRUE; - wpa_printf(MSG_DEBUG, "KaY: I is elected as key server"); + wpa_printf(MSG_DEBUG, "KaY: I am elected as key server"); participant->to_dist_sak = FALSE; participant->is_elected = TRUE; @@ -2163,6 +2284,9 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) sizeof(kay->key_server_sci)); kay->key_server_priority = kay->actor_priority; } else if (key_server) { + wpa_printf(MSG_DEBUG, + "KaY: Peer %s was elected as the key server", + mi_txt(key_server->mi)); ieee802_1x_cp_set_electedself(kay->cp, FALSE); if (!sci_equal(&kay->key_server_sci, &key_server->sci)) { ieee802_1x_cp_signal_chgdserver(kay->cp); @@ -2281,11 +2405,19 @@ ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant, os_memcpy(ether_hdr->src, participant->kay->actor_sci.addr, sizeof(ether_hdr->dest)); ether_hdr->ethertype = host_to_be16(ETH_P_EAPOL); + wpa_printf(MSG_DEBUG, "KaY: Ethernet header: DA=" MACSTR " SA=" MACSTR + " Ethertype=0x%x", + MAC2STR(ether_hdr->dest), MAC2STR(ether_hdr->src), + be_to_host16(ether_hdr->ethertype)); eapol_hdr = wpabuf_put(pbuf, sizeof(*eapol_hdr)); eapol_hdr->version = EAPOL_VERSION; eapol_hdr->type = IEEE802_1X_TYPE_EAPOL_MKA; - eapol_hdr->length = host_to_be16(pbuf->size - pbuf->used); + eapol_hdr->length = host_to_be16(wpabuf_tailroom(pbuf)); + wpa_printf(MSG_DEBUG, + "KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u", + eapol_hdr->version, eapol_hdr->type, + be_to_host16(eapol_hdr->length)); for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) { if (mka_body_handler[i].body_present && @@ -2298,6 +2430,7 @@ ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant, return 0; } + /** * ieee802_1x_participant_send_mkpdu - */ @@ -2310,7 +2443,8 @@ ieee802_1x_participant_send_mkpdu( size_t length = 0; unsigned int i; - wpa_printf(MSG_DEBUG, "KaY: to enpacket and send the MKPDU"); + wpa_printf(MSG_DEBUG, "KaY: Encode and send an MKPDU (ifname=%s)", + kay->if_name); length += sizeof(struct ieee802_1x_hdr) + sizeof(struct ieee8023_hdr); for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) { if (mka_body_handler[i].body_present && @@ -2325,10 +2459,11 @@ ieee802_1x_participant_send_mkpdu( } if (ieee802_1x_kay_encode_mkpdu(participant, buf)) { - wpa_printf(MSG_ERROR, "KaY: encode mkpdu fail!"); + wpa_printf(MSG_ERROR, "KaY: encode mkpdu fail"); return -1; } + wpa_hexdump_buf(MSG_MSGDUMP, "KaY: Outgoing MKPDU", buf); l2_packet_send(kay->l2_mka, NULL, 0, wpabuf_head(buf), wpabuf_len(buf)); wpabuf_free(buf); @@ -2365,6 +2500,8 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) participant = (struct ieee802_1x_mka_participant *)eloop_ctx; kay = participant->kay; + wpa_printf(MSG_DEBUG, "KaY: Participant timer (ifname=%s)", + kay->if_name); if (participant->cak_life) { if (now > participant->cak_life) goto delete_mka; @@ -2452,7 +2589,7 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) } } - if (participant->new_sak) { + if (participant->new_sak && participant->is_key_server) { if (!ieee802_1x_kay_generate_new_sak(participant)) participant->to_dist_sak = TRUE; @@ -2465,7 +2602,7 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) participant->retry_count++; } - eloop_register_timeout(MKA_HELLO_TIME / 1000, 0, + eloop_register_timeout(kay->mka_hello_time / 1000, 0, ieee802_1x_participant_timer, participant, NULL); @@ -2514,7 +2651,7 @@ ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN, dl_list_add(&psc->sa_list, &psa->list); wpa_printf(MSG_DEBUG, - "KaY: Create transmit SA(an: %hhu, next_PN: %u) of SC", + "KaY: Create transmit SA(an: %hhu, next_pn: %u) of SC", an, next_PN); return psa; @@ -2557,8 +2694,8 @@ ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci) psc->enciphering_sa = FALSE; dl_list_init(&psc->sa_list); - wpa_printf(MSG_DEBUG, "KaY: Create transmit SC"); - wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)sci , sizeof(*sci)); + wpa_printf(MSG_DEBUG, "KaY: Create transmit SC - SCI: %s", + sci_txt(&psc->sci)); return psc; } @@ -2709,7 +2846,7 @@ int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay, } } if (!latest_sak) { - wpa_printf(MSG_ERROR, "lki related sak not found"); + wpa_printf(MSG_ERROR, "KaY: lki related sak not found"); return -1; } @@ -2730,7 +2867,9 @@ int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay, ieee802_1x_delete_transmit_sa(kay, txsa); txsa = ieee802_1x_kay_init_transmit_sa(principal->txsc, latest_sak->an, - 1, latest_sak); + latest_sak->next_pn ? + latest_sak->next_pn : 1, + latest_sak); if (!txsa) return -1; @@ -2779,12 +2918,12 @@ int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay, dl_list_for_each_safe(sa_key, pre_key, &principal->sak_list, struct data_key, list) { if (is_ki_equal(&sa_key->key_identifier, ki)) { + if (principal->new_key == sa_key) + principal->new_key = NULL; dl_list_del(&sa_key->list); ieee802_1x_kay_deinit_data_key(sa_key); break; } - if (principal->new_key == sa_key) - principal->new_key = NULL; } return 0; @@ -2872,7 +3011,8 @@ int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay) /** * ieee802_1x_kay_mkpdu_sanity_check - - * sanity check specified in clause 11.11.2 of IEEE802.1X-2010 + * Sanity checks specified in IEEE Std 802.1X-2010, 11.11.2 (Validation of + * MKPDUs) */ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, const u8 *buf, size_t len) @@ -2886,34 +3026,49 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, size_t body_len; size_t ckn_len; u8 icv[MAX_ICV_LEN]; - u8 *msg_icv; + const u8 *msg_icv; + /* len > eth+eapol header already verified in kay_l2_receive(); + * likewise, eapol_hdr->length validated there */ eth_hdr = (struct ieee8023_hdr *) buf; eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1); mka_hdr = (struct ieee802_1x_mka_hdr *) (eapol_hdr + 1); - /* destination address should be not individual address */ + wpa_printf(MSG_DEBUG, "KaY: Ethernet header: DA=" MACSTR " SA=" MACSTR + " Ethertype=0x%x", + MAC2STR(eth_hdr->dest), MAC2STR(eth_hdr->src), + be_to_host16(eth_hdr->ethertype)); + + /* the destination address shall not be an individual address */ if (os_memcmp(eth_hdr->dest, pae_group_addr, ETH_ALEN) != 0) { - wpa_printf(MSG_MSGDUMP, + wpa_printf(MSG_DEBUG, "KaY: ethernet destination address is not PAE group address"); return -1; } - /* MKPDU should not be less than 32 octets */ + wpa_printf(MSG_DEBUG, + "KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u", + eapol_hdr->version, eapol_hdr->type, + be_to_host16(eapol_hdr->length)); + + /* MKPDU shall not be less than 32 octets */ mka_msg_len = be_to_host16(eapol_hdr->length); if (mka_msg_len < 32) { - wpa_printf(MSG_MSGDUMP, "KaY: MKPDU is less than 32 octets"); + wpa_printf(MSG_DEBUG, "KaY: MKPDU is less than 32 octets"); return -1; } - /* MKPDU should be a multiple of 4 octets */ + /* MKPDU shall be a multiple of 4 octets */ if ((mka_msg_len % 4) != 0) { - wpa_printf(MSG_MSGDUMP, + wpa_printf(MSG_DEBUG, "KaY: MKPDU is not multiple of 4 octets"); return -1; } + wpa_hexdump(MSG_MSGDUMP, "KaY: EAPOL-MKA Packet Body (MKPDU)", + mka_hdr, mka_msg_len); + + /* Room for body_len already verified in kay_l2_receive() */ body = (struct ieee802_1x_mka_basic_body *) mka_hdr; - ieee802_1x_mka_dump_basic_body(body); body_len = get_mka_param_body_len(body); /* EAPOL-MKA body should comprise basic parameter set and ICV */ if (mka_msg_len < MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN) { @@ -2932,24 +3087,27 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, ckn_len = body_len - (sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN); if (ckn_len < 1 || ckn_len > MAX_CKN_LEN) { - wpa_printf(MSG_ERROR, + wpa_printf(MSG_WARNING, "KaY: Received EAPOL-MKA CKN Length (%zu bytes) is out of range (<= %u bytes)", ckn_len, MAX_CKN_LEN); return -1; } + ieee802_1x_mka_dump_basic_body(body); + /* CKN should be owned by I */ participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len); if (!participant) { - wpa_printf(MSG_DEBUG, "CKN is not included in my CA"); + wpa_printf(MSG_DEBUG, "KaY: CKN is not included in my CA"); return -1; } /* algorithm agility check */ if (os_memcmp(body->algo_agility, mka_algo_agility, sizeof(body->algo_agility)) != 0) { - wpa_printf(MSG_ERROR, - "KaY: peer's algorithm agility not supported for me"); + wpa_printf(MSG_INFO, + "KaY: Peer's algorithm agility (%s) not supported", + algo_agility_txt(body->algo_agility)); return -1; } @@ -2959,23 +3117,29 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, * its size, not the fixed length 16 octets, indicated by the EAPOL * packet body length. */ - if (mka_alg_tbl[kay->mka_algindex].icv_hash( - participant->ick.key, + if (len < mka_alg_tbl[kay->mka_algindex].icv_len || + mka_alg_tbl[kay->mka_algindex].icv_hash( + participant->ick.key, participant->ick.len, buf, len - mka_alg_tbl[kay->mka_algindex].icv_len, icv)) { - wpa_printf(MSG_ERROR, "KaY: omac1_aes_128 failed"); + wpa_printf(MSG_ERROR, "KaY: Failed to calculate ICV"); return -1; } - msg_icv = ieee802_1x_mka_decode_icv_body(participant, (u8 *) mka_hdr, + msg_icv = ieee802_1x_mka_decode_icv_body(participant, + (const u8 *) mka_hdr, mka_msg_len); if (!msg_icv) { - wpa_printf(MSG_ERROR, "KaY: No ICV"); + wpa_printf(MSG_WARNING, "KaY: No ICV in MKPDU - ignore it"); return -1; } + wpa_hexdump(MSG_DEBUG, "KaY: Received ICV", + msg_icv, mka_alg_tbl[kay->mka_algindex].icv_len); if (os_memcmp_const(msg_icv, icv, mka_alg_tbl[kay->mka_algindex].icv_len) != 0) { - wpa_printf(MSG_ERROR, + wpa_printf(MSG_WARNING, "KaY: Computed ICV is not equal to Received ICV"); + wpa_hexdump(MSG_DEBUG, "KaY: Calculated ICV", + icv, mka_alg_tbl[kay->mka_algindex].icv_len); return -1; } @@ -2991,13 +3155,19 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, { struct ieee802_1x_mka_participant *participant; struct ieee802_1x_mka_hdr *hdr; + struct ieee802_1x_kay_peer *peer; size_t body_len; size_t left_len; u8 body_type; int i; const u8 *pos; Boolean handled[256]; + Boolean bad_sak_use = FALSE; /* Error detected while processing SAK Use + * parameter set */ + Boolean i_in_peerlist, is_in_live_peer, is_in_potential_peer; + wpa_printf(MSG_DEBUG, "KaY: Decode received MKPDU (ifname=%s)", + kay->if_name); if (ieee802_1x_kay_mkpdu_sanity_check(kay, buf, len)) return -1; @@ -3011,17 +3181,24 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, /* to skip basic parameter set */ hdr = (struct ieee802_1x_mka_hdr *) pos; - body_len = get_mka_param_body_len(hdr); + body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr)); + if (left_len < body_len + MKA_HDR_LEN) + return -1; pos += body_len + MKA_HDR_LEN; left_len -= body_len + MKA_HDR_LEN; /* check i am in the peer's peer list */ - if (ieee802_1x_mka_i_in_peerlist(participant, pos, left_len) && - !ieee802_1x_kay_is_in_live_peer(participant, - participant->current_peer_id.mi)) { + i_in_peerlist = ieee802_1x_mka_i_in_peerlist(participant, pos, + left_len); + is_in_live_peer = ieee802_1x_kay_is_in_live_peer( + participant, participant->current_peer_id.mi); + wpa_printf(MSG_DEBUG, "KaY: i_in_peerlist=%s is_in_live_peer=%s", + yes_no(i_in_peerlist), yes_no(is_in_live_peer)); + if (i_in_peerlist && !is_in_live_peer) { /* accept the peer as live peer */ - if (ieee802_1x_kay_is_in_potential_peer( - participant, participant->current_peer_id.mi)) { + is_in_potential_peer = ieee802_1x_kay_is_in_potential_peer( + participant, participant->current_peer_id.mi); + if (is_in_potential_peer) { if (!ieee802_1x_kay_move_live_peer( participant, participant->current_peer_id.mi, @@ -3051,7 +3228,7 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, pos += body_len + MKA_HDR_LEN, left_len -= body_len + MKA_HDR_LEN) { hdr = (struct ieee802_1x_mka_hdr *) pos; - body_len = get_mka_param_body_len(hdr); + body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr)); body_type = get_mka_param_body_type(hdr); if (body_type == MKA_ICV_INDICATOR) @@ -3065,21 +3242,103 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, return -1; } - if (handled[body_type]) + if (handled[body_type]) { + wpa_printf(MSG_DEBUG, + "KaY: Ignore duplicated body type %u", + body_type); continue; + } handled[body_type] = TRUE; if (body_type < ARRAY_SIZE(mka_body_handler) && mka_body_handler[body_type].body_rx) { - mka_body_handler[body_type].body_rx - (participant, pos, left_len); + if (mka_body_handler[body_type].body_rx + (participant, pos, left_len) != 0) { + /* Handle parameter set failure */ + if (body_type != MKA_SAK_USE) { + wpa_printf(MSG_INFO, + "KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed", + body_type); + return -1; + } + + /* Ideally DIST-SAK should be processed before + * SAK-USE. Unfortunately IEEE Std 802.1X-2010, + * 11.11.3 (Encoding MKPDUs) states SAK-USE(3) + * must always be encoded before DIST-SAK(4). + * Rather than redesigning mka_body_handler so + * that it somehow processes DIST-SAK before + * SAK-USE, just ignore SAK-USE failures if + * DIST-SAK is also present in this MKPDU. */ + bad_sak_use = TRUE; + } } else { wpa_printf(MSG_ERROR, - "The type %d is not supported in this MKA version %d", + "KaY: The body type %d is not supported in this MKA version %d", body_type, MKA_VERSION_ID); } } + if (bad_sak_use && !handled[MKA_DISTRIBUTED_SAK]) { + wpa_printf(MSG_INFO, + "KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed", + MKA_SAK_USE); + if (!reset_participant_mi(participant)) + wpa_printf(MSG_DEBUG, "KaY: Could not update mi"); + else + wpa_printf(MSG_DEBUG, + "KaY: Selected a new random MI: %s", + mi_txt(participant->mi)); + return -1; + } + + /* Detect missing parameter sets */ + peer = ieee802_1x_kay_get_live_peer(participant, + participant->current_peer_id.mi); + if (peer) { + /* MKPDU is from live peer */ + if (!handled[MKA_SAK_USE]) { + /* Once a live peer starts sending SAK-USE, it should be + * sent every time. */ + if (peer->sak_used) { + wpa_printf(MSG_INFO, + "KaY: Discarding Rx MKPDU: Live Peer stopped sending SAK-USE"); + return -1; + } + + /* Live peer is probably hung if it hasn't sent SAK-USE + * after a reasonable number of MKPDUs. Drop the MKPDU, + * which will eventually force an timeout. */ + if (++peer->missing_sak_use_count > + MAX_MISSING_SAK_USE) { + wpa_printf(MSG_INFO, + "KaY: Discarding Rx MKPDU: Live Peer not sending SAK-USE"); + return -1; + } + } else { + peer->missing_sak_use_count = 0; + + /* Only update live peer watchdog after successful + * decode of all parameter sets */ + peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; + } + } else { + /* MKPDU is from new or potential peer */ + peer = ieee802_1x_kay_get_peer(participant, + participant->current_peer_id.mi); + if (!peer) { + wpa_printf(MSG_DEBUG, "KaY: No peer entry found"); + return -1; + } + + /* Do not update potential peer watchdog. Per IEEE Std + * 802.1X-2010, 9.4.3, potential peers need to show liveness by + * including our MI/MN in their transmitted MKPDU (within + * potential or live parameter sets). Whena potential peer does + * include our MI/MN in an MKPDU, we respond by moving the peer + * from 'potential_peers' to 'live_peers'. */ + } + kay->active = TRUE; participant->retry_count = 0; participant->active = TRUE; @@ -3095,6 +3354,9 @@ static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf, struct ieee802_1x_kay *kay = ctx; struct ieee8023_hdr *eth_hdr; struct ieee802_1x_hdr *eapol_hdr; + size_t calc_len; + + /* IEEE Std 802.1X-2010, 11.4 (Validation of received EAPOL PDUs) */ /* must contain at least ieee8023_hdr + ieee802_1x_hdr */ if (len < sizeof(*eth_hdr) + sizeof(*eapol_hdr)) { @@ -3105,13 +3367,21 @@ static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf, eth_hdr = (struct ieee8023_hdr *) buf; eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1); - if (len != sizeof(*eth_hdr) + sizeof(*eapol_hdr) + - be_to_host16(eapol_hdr->length)) { - wpa_printf(MSG_MSGDUMP, "KAY: EAPOL MPDU is invalid: (%lu-%lu)", + calc_len = sizeof(*eth_hdr) + sizeof(*eapol_hdr) + + be_to_host16(eapol_hdr->length); + if (len < calc_len) { + wpa_printf(MSG_MSGDUMP, "KaY: EAPOL MPDU is invalid: (received len %lu, calculated len %lu, EAPOL length %u)", (unsigned long) len, - (unsigned long) be_to_host16(eapol_hdr->length)); + (unsigned long) calc_len, + be_to_host16(eapol_hdr->length)); return; } + if (len > calc_len) { + wpa_hexdump(MSG_DEBUG, + "KaY: Ignore extra octets following the Packey Body field", + &buf[calc_len], len - calc_len); + len = calc_len; + } if (eapol_hdr->version < EAPOL_VERSION) { wpa_printf(MSG_MSGDUMP, "KaY: version %d does not support MKA", @@ -3120,11 +3390,12 @@ static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf, } if (be_to_host16(eth_hdr->ethertype) != ETH_P_PAE || eapol_hdr->type != IEEE802_1X_TYPE_EAPOL_MKA) - return; + return; /* ignore other EAPOL types silently here */ - wpa_hexdump(MSG_DEBUG, "RX EAPOL-MKA: ", buf, len); + wpa_hexdump(MSG_DEBUG, "KaY: RX EAPOL-MKA", buf, len); if (dl_list_empty(&kay->participant_list)) { - wpa_printf(MSG_ERROR, "KaY: no MKA participant instance"); + wpa_printf(MSG_ERROR, + "KaY: No MKA participant instance - ignore EAPOL-MKA"); return; } @@ -3137,10 +3408,14 @@ static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf, */ struct ieee802_1x_kay * ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, + Boolean macsec_replay_protect, u32 macsec_replay_window, u16 port, u8 priority, const char *ifname, const u8 *addr) { struct ieee802_1x_kay *kay; + wpa_printf(MSG_DEBUG, "KaY: Initialize - ifname=%s addr=" MACSTR + " port=%u priority=%u", + ifname, MAC2STR(addr), port, priority); kay = os_zalloc(sizeof(*kay)); if (!kay) { wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__); @@ -3161,6 +3436,8 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, os_strlcpy(kay->if_name, ifname, IFNAMSIZ); os_memcpy(kay->actor_sci.addr, addr, ETH_ALEN); kay->actor_sci.port = host_to_be16(port ? port : 0x0001); + wpa_printf(MSG_DEBUG, "KaY: Generated SCI: %s", + sci_txt(&kay->actor_sci)); kay->actor_priority = priority; /* While actor acts as a key server, shall distribute sakey */ @@ -3187,21 +3464,27 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED; kay->macsec_desired = FALSE; kay->macsec_protect = FALSE; + kay->macsec_encrypt = FALSE; kay->macsec_validate = Disabled; kay->macsec_replay_protect = FALSE; kay->macsec_replay_window = 0; kay->macsec_confidentiality = CONFIDENTIALITY_NONE; + kay->mka_hello_time = MKA_HELLO_TIME; } else { kay->macsec_desired = TRUE; kay->macsec_protect = TRUE; - kay->macsec_encrypt = policy == SHOULD_ENCRYPT; - kay->macsec_validate = Strict; - kay->macsec_replay_protect = FALSE; - kay->macsec_replay_window = 0; - if (kay->macsec_capable >= MACSEC_CAP_INTEG_AND_CONF) + if (kay->macsec_capable >= MACSEC_CAP_INTEG_AND_CONF && + policy == SHOULD_ENCRYPT) { + kay->macsec_encrypt = TRUE; kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0; - else + } else { /* SHOULD_SECURE */ + kay->macsec_encrypt = FALSE; kay->macsec_confidentiality = CONFIDENTIALITY_NONE; + } + kay->macsec_validate = Strict; + kay->macsec_replay_protect = macsec_replay_protect; + kay->macsec_replay_window = macsec_replay_window; + kay->mka_hello_time = MKA_HELLO_TIME; } wpa_printf(MSG_DEBUG, "KaY: state machine created"); @@ -3273,6 +3556,19 @@ ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay) } +static const char * mode_txt(enum mka_created_mode mode) +{ + switch (mode) { + case PSK: + return "PSK"; + case EAP_EXCHANGE: + return "EAP"; + } + + return "?"; +} + + /** * ieee802_1x_kay_create_mka - */ @@ -3285,17 +3581,22 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct ieee802_1x_mka_participant *participant; unsigned int usecs; + wpa_printf(MSG_DEBUG, + "KaY: Create MKA (ifname=%s mode=%s authenticator=%s)", + kay->if_name, mode_txt(mode), yes_no(is_authenticator)); + if (!kay || !ckn || !cak) { wpa_printf(MSG_ERROR, "KaY: ckn or cak is null"); return NULL; } - if (cak->len != mka_alg_tbl[kay->mka_algindex].cak_len) { - wpa_printf(MSG_ERROR, "KaY: CAK length not follow key schema"); + if (cak->len != 16 && cak->len != 32) { + wpa_printf(MSG_ERROR, "KaY: Unexpected CAK length %u", + (unsigned int) cak->len); return NULL; } if (ckn->len > MAX_CKN_LEN) { - wpa_printf(MSG_ERROR, "KaY: CKN is out of range(<=32 bytes)"); + wpa_printf(MSG_ERROR, "KaY: CKN is out of range (>32 bytes)"); return NULL; } if (!kay->enable) { @@ -3311,8 +3612,12 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, participant->ckn.len = ckn->len; os_memcpy(participant->ckn.name, ckn->name, ckn->len); + wpa_hexdump(MSG_DEBUG, "KaY: CKN", participant->ckn.name, + participant->ckn.len); participant->cak.len = cak->len; os_memcpy(participant->cak.key, cak->key, cak->len); + wpa_hexdump_key(MSG_DEBUG, "KaY: CAK", participant->cak.key, + participant->cak.len); if (life) participant->cak_life = life + time(NULL); @@ -3362,6 +3667,8 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, if (!reset_participant_mi(participant)) goto fail; + wpa_printf(MSG_DEBUG, "KaY: Selected random MI: %s", + mi_txt(participant->mi)); participant->lrx = FALSE; participant->ltx = FALSE; @@ -3377,37 +3684,40 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, secy_cp_control_protect_frames(kay, kay->macsec_protect); secy_cp_control_replay(kay, kay->macsec_replay_protect, kay->macsec_replay_window); - secy_create_transmit_sc(kay, participant->txsc); + if (secy_create_transmit_sc(kay, participant->txsc)) + goto fail; /* to derive KEK from CAK and CKN */ - participant->kek.len = mka_alg_tbl[kay->mka_algindex].kek_len; + participant->kek.len = participant->cak.len; if (mka_alg_tbl[kay->mka_algindex].kek_trfm(participant->cak.key, + participant->cak.len, participant->ckn.name, participant->ckn.len, - participant->kek.key)) { - wpa_printf(MSG_ERROR, "KaY: Derived KEK failed"); + participant->kek.key, + participant->kek.len)) { + wpa_printf(MSG_ERROR, "KaY: KEK derivation failed"); goto fail; } wpa_hexdump_key(MSG_DEBUG, "KaY: Derived KEK", participant->kek.key, participant->kek.len); /* to derive ICK from CAK and CKN */ - participant->ick.len = mka_alg_tbl[kay->mka_algindex].ick_len; + participant->ick.len = participant->cak.len; if (mka_alg_tbl[kay->mka_algindex].ick_trfm(participant->cak.key, + participant->cak.len, participant->ckn.name, participant->ckn.len, - participant->ick.key)) { - wpa_printf(MSG_ERROR, "KaY: Derived ICK failed"); + participant->ick.key, + participant->ick.len)) { + wpa_printf(MSG_ERROR, "KaY: ICK derivation failed"); goto fail; } wpa_hexdump_key(MSG_DEBUG, "KaY: Derived ICK", participant->ick.key, participant->ick.len); dl_list_add(&kay->participant_list, &participant->list); - wpa_hexdump(MSG_DEBUG, "KaY: Participant created:", - ckn->name, ckn->len); - usecs = os_random() % (MKA_HELLO_TIME * 1000); + usecs = os_random() % (kay->mka_hello_time * 1000); eloop_register_timeout(0, usecs, ieee802_1x_participant_timer, participant, NULL); @@ -3425,6 +3735,7 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, return participant; fail: + os_free(participant->txsc); os_free(participant); return NULL; } @@ -3580,6 +3891,7 @@ ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, #ifdef CONFIG_CTRL_IFACE + /** * ieee802_1x_kay_get_status - Get IEEE 802.1X KaY status details * @sm: Pointer to KaY allocated with ieee802_1x_kay_init() @@ -3588,19 +3900,24 @@ ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, * @verbose: Whether to include verbose status information * Returns: Number of bytes written to buf. * - * Query KAY status information. This function fills in a text area with current + * Query KaY status information. This function fills in a text area with current * status information. If the buffer (buf) is not large enough, status * information will be truncated to fit the buffer. */ int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf, size_t buflen) { - int len; + char *pos, *end; + int res, count; + struct ieee802_1x_mka_participant *p; if (!kay) return 0; - len = os_snprintf(buf, buflen, + pos = buf; + end = buf + buflen; + + res = os_snprintf(pos, end - pos, "PAE KaY status=%s\n" "Authenticated=%s\n" "Secured=%s\n" @@ -3609,7 +3926,8 @@ int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf, "Key Server Priority=%u\n" "Is Key Server=%s\n" "Number of Keys Distributed=%u\n" - "Number of Keys Received=%u\n", + "Number of Keys Received=%u\n" + "MKA Hello Time=%u\n", kay->active ? "Active" : "Not-Active", kay->authenticated ? "Yes" : "No", kay->secured ? "Yes" : "No", @@ -3618,10 +3936,162 @@ int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf, kay->key_server_priority, kay->is_key_server ? "Yes" : "No", kay->dist_kn - 1, - kay->rcvd_keys); - if (os_snprintf_error(buflen, len)) + kay->rcvd_keys, + kay->mka_hello_time); + if (os_snprintf_error(buflen, res)) + return 0; + pos += res; + + res = os_snprintf(pos, end - pos, + "actor_sci=%s\n", sci_txt(&kay->actor_sci)); + if (os_snprintf_error(buflen, res)) + return end - pos; + pos += res; + + res = os_snprintf(pos, end - pos, + "key_server_sci=%s\n", sci_txt(&kay->key_server_sci)); + if (os_snprintf_error(buflen, res)) + return end - pos; + pos += res; + + count = 0; + dl_list_for_each(p, &kay->participant_list, + struct ieee802_1x_mka_participant, list) { + char *pos2 = pos; + + res = os_snprintf(pos2, end - pos2, "participant_idx=%d\nckn=", + count); + if (os_snprintf_error(buflen, res)) + return end - pos; + pos2 += res; + count++; + + pos2 += wpa_snprintf_hex(pos2, end - pos2, p->ckn.name, + p->ckn.len); + + res = os_snprintf(pos2, end - pos2, + "\nmi=%s\n" + "mn=%u\n" + "active=%s\n" + "participant=%s\n" + "retain=%s\n" + "live_peers=%u\n" + "potential_peers=%u\n" + "is_key_server=%s\n" + "is_elected=%s\n", + mi_txt(p->mi), p->mn, + yes_no(p->active), + yes_no(p->participant), + yes_no(p->retain), + dl_list_len(&p->live_peers), + dl_list_len(&p->potential_peers), + yes_no(p->is_key_server), + yes_no(p->is_elected)); + if (os_snprintf_error(buflen, res)) + return end - pos; + pos2 += res; + pos = pos2; + } + + return pos - buf; +} + + +static const char * true_false(Boolean val) +{ + return val ? "true" : "false"; +} + + +static const char * activate_control_txt(enum activate_ctrl activate) +{ + switch (activate) { + case DEFAULT: + return "default"; + case DISABLED: + return "disabled"; + case ON_OPER_UP: + return "onOperUp"; + case ALWAYS: + return "always"; + } + + return "?"; +} + + +static char * mka_mib_peer(struct dl_list *peers, Boolean live, char *buf, + char *end) +{ + char *pos = buf; + struct ieee802_1x_kay_peer *p; + int res; + + dl_list_for_each(p, peers, struct ieee802_1x_kay_peer, list) { + res = os_snprintf(pos, end - pos, + "ieee8021XKayMkaPeerListMI=%s\n" + "ieee8021XKayMkaPeerListMN=%u\n" + "ieee8021XKayMkaPeerListType=%u\n" + "ieee8021XKayMkaPeerListSCI=%s\n", + mi_txt(p->mi), + p->mn, + live ? 1 : 2, + sci_txt(&p->sci)); + if (os_snprintf_error(end - pos, res)) + return pos; + pos += res; + } + + return pos; +} + + +int ieee802_1x_kay_get_mib(struct ieee802_1x_kay *kay, char *buf, + size_t buflen) +{ + char *pos, *end; + int res; + struct ieee802_1x_mka_participant *p; + + if (!kay) return 0; - return len; + pos = buf; + end = buf + buflen; + + dl_list_for_each(p, &kay->participant_list, + struct ieee802_1x_mka_participant, list) { + char *pos2 = pos; + + res = os_snprintf(pos2, end - pos2, "ieee8021XKayMkaPartCKN="); + if (os_snprintf_error(buflen, res)) + return end - pos; + pos2 += res; + + pos2 += wpa_snprintf_hex(pos2, end - pos2, p->ckn.name, + p->ckn.len); + + res = os_snprintf(pos2, end - pos2, + "\nieee8021XKayMkaPartCached=%s\n" + "ieee8021XKayMkaPartActive=%s\n" + "ieee8021XKayMkaPartRetain=%s\n" + "ieee8021XKayMkaPartActivateControl=%s\n" + "ieee8021XKayMkaPartPrincipal=%s\n", + true_false(p->cached), + true_false(p->active), + true_false(p->retain), + activate_control_txt(p->activate), + true_false(p->principal)); + if (os_snprintf_error(buflen, res)) + return end - pos; + pos2 += res; + pos = pos2; + + pos = mka_mib_peer(&p->live_peers, TRUE, pos, end); + pos = mka_mib_peer(&p->potential_peers, FALSE, pos, end); + } + + return pos - buf; } + #endif /* CONFIG_CTRL_IFACE */ diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h index b2650596cae3c..3367d3aaa8c1a 100644 --- a/src/pae/ieee802_1x_kay.h +++ b/src/pae/ieee802_1x_kay.h @@ -21,6 +21,7 @@ struct macsec_init_params; /* MKA timer, unit: millisecond */ #define MKA_HELLO_TIME 2000 +#define MKA_BOUNDED_HELLO_TIME 500 #define MKA_LIFE_TIME 6000 #define MKA_SAK_RETIRE_TIME 3000 @@ -38,7 +39,7 @@ struct ieee802_1x_mka_ki { struct ieee802_1x_mka_sci { u8 addr[ETH_ALEN]; be16 port; -}; +} STRUCT_PACKED; struct mka_key { u8 key[MAX_KEY_LEN]; @@ -149,6 +150,7 @@ struct ieee802_1x_kay_ctx { int (*get_receive_lowest_pn)(void *ctx, struct receive_sa *sa); int (*get_transmit_next_pn)(void *ctx, struct transmit_sa *sa); int (*set_transmit_next_pn)(void *ctx, struct transmit_sa *sa); + int (*set_receive_lowest_pn)(void *ctx, struct receive_sa *sa); int (*create_receive_sc)(void *ctx, struct receive_sc *sc, enum validate_frames vf, enum confidentiality_offset co); @@ -187,6 +189,7 @@ struct ieee802_1x_kay { u32 macsec_replay_window; enum validate_frames macsec_validate; enum confidentiality_offset macsec_confidentiality; + u32 mka_hello_time; u32 ltx_kn; u8 ltx_an; @@ -236,6 +239,7 @@ u64 mka_sci_u64(struct ieee802_1x_mka_sci *sci); struct ieee802_1x_kay * ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, + Boolean macsec_replay_protect, u32 macsec_replay_window, u16 port, u8 priority, const char *ifname, const u8 *addr); void ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay); @@ -271,5 +275,7 @@ int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay, int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay); int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf, size_t buflen); +int ieee802_1x_kay_get_mib(struct ieee802_1x_kay *kay, char *buf, + size_t buflen); #endif /* IEEE802_1X_KAY_H */ diff --git a/src/pae/ieee802_1x_kay_i.h b/src/pae/ieee802_1x_kay_i.h index bc522d89852bf..f9cd3f41b093b 100644 --- a/src/pae/ieee802_1x_kay_i.h +++ b/src/pae/ieee802_1x_kay_i.h @@ -15,7 +15,7 @@ #define MKA_VERSION_ID 1 -/* IEEE Std 802.1X-2010, 11.11.1, Table 11-7 */ +/* IEEE Std 802.1X-2010, 11.11.1, Table 11-7 (MKPDU parameter sets) */ enum mka_packet_type { MKA_BASIC_PARAMETER_SET = MKA_VERSION_ID, MKA_LIVE_PEER_LIST = 1, @@ -39,7 +39,7 @@ struct ieee802_1x_kay; struct ieee802_1x_mka_peer_id { u8 mi[MI_LEN]; be32 mn; -}; +} STRUCT_PACKED; struct ieee802_1x_kay_peer { struct ieee802_1x_mka_sci sci; @@ -51,6 +51,7 @@ struct ieee802_1x_kay_peer { Boolean macsec_desired; enum macsec_cap macsec_capability; Boolean sak_used; + int missing_sak_use_count; struct dl_list list; }; @@ -59,25 +60,24 @@ struct macsec_ciphersuite { char name[32]; enum macsec_cap capable; int sak_len; /* unit: byte */ - - u32 index; }; struct mka_alg { u8 parameter[4]; - size_t cak_len; - size_t kek_len; - size_t ick_len; size_t icv_len; - int (*cak_trfm)(const u8 *msk, const u8 *mac1, const u8 *mac2, u8 *cak); - int (*ckn_trfm)(const u8 *msk, const u8 *mac1, const u8 *mac2, - const u8 *sid, size_t sid_len, u8 *ckn); - int (*kek_trfm)(const u8 *cak, const u8 *ckn, size_t ckn_len, u8 *kek); - int (*ick_trfm)(const u8 *cak, const u8 *ckn, size_t ckn_len, u8 *ick); - int (*icv_hash)(const u8 *ick, const u8 *msg, size_t msg_len, u8 *icv); - - int index; /* index for configuring */ + int (*cak_trfm)(const u8 *msk, size_t msk_bytes, const u8 *mac1, + const u8 *mac2, u8 *cak, size_t cak_bytes); + int (*ckn_trfm)(const u8 *msk, size_t msk_bytes, const u8 *mac1, + const u8 *mac2, const u8 *sid, size_t sid_len, u8 *ckn); + int (*kek_trfm)(const u8 *cak, size_t cak_bytes, + const u8 *ckn, size_t ckn_len, + u8 *kek, size_t kek_bytes); + int (*ick_trfm)(const u8 *cak, size_t cak_bytes, + const u8 *ckn, size_t ckn_len, + u8 *ick, size_t ick_bytes); + int (*icv_hash)(const u8 *ick, size_t ick_bytes, + const u8 *msg, size_t msg_len, u8 *icv); }; #define DEFAULT_MKA_ALG_INDEX 0 @@ -95,7 +95,7 @@ struct ieee802_1x_mka_participant { Boolean retain; enum mka_created_mode mode; - enum { DEFAULT, DISABLED, ON_OPER_UP, ALWAYS } activate; + enum activate_ctrl { DEFAULT, DISABLED, ON_OPER_UP, ALWAYS } activate; /* used for active participant */ Boolean principal; @@ -131,8 +131,10 @@ struct ieee802_1x_mka_participant { u8 mi[MI_LEN]; u32 mn; + /* Current peer MI and SCI during MKPDU processing */ struct ieee802_1x_mka_peer_id current_peer_id; struct ieee802_1x_mka_sci current_peer_sci; + time_t cak_life; time_t mka_life; Boolean to_dist_sak; @@ -165,7 +167,7 @@ struct ieee802_1x_mka_hdr { #endif /* octet 4 */ u8 length1; -}; +} STRUCT_PACKED; #define MKA_HDR_LEN sizeof(struct ieee802_1x_mka_hdr) @@ -210,9 +212,9 @@ struct ieee802_1x_mka_basic_body { be32 actor_mn; u8 algo_agility[4]; - /* followed by CAK Name*/ + /* followed by CAK Name */ u8 ckn[0]; -}; +} STRUCT_PACKED; /** * struct ieee802_1x_mka_peer_body - Live Peer List and Potential Peer List @@ -238,9 +240,9 @@ struct ieee802_1x_mka_peer_body { /* octet 4 */ u8 length1; - u8 peer[0]; /* followed by Peers */ -}; + u8 peer[0]; +} STRUCT_PACKED; /** * struct ieee802_1x_mka_sak_use_body - MACsec SAK Use parameter set (Figure @@ -315,7 +317,7 @@ struct ieee802_1x_mka_sak_use_body { be32 okn; /* octet 41 - 44 */ be32 olpn; -}; +} STRUCT_PACKED; /** * struct ieee802_1x_mka_dist_sak_body - Distributed SAK parameter set @@ -362,7 +364,7 @@ struct ieee802_1x_mka_dist_sak_body { * for other cipher suite: octet 9-16: cipher suite id, octet 17-: SAK */ u8 sak[0]; -}; +} STRUCT_PACKED; /** * struct ieee802_1x_mka_dist_cak_body - Distributed CAK parameter set (Figure @@ -398,7 +400,7 @@ struct ieee802_1x_mka_dist_cak_body { /* followed by CAK Name, 29- */ u8 ckn[0]; -}; +} STRUCT_PACKED; struct ieee802_1x_mka_icv_body { /* octet 1 */ @@ -418,6 +420,6 @@ struct ieee802_1x_mka_icv_body { /* octet 5 - */ u8 icv[0]; -}; +} STRUCT_PACKED; #endif /* IEEE802_1X_KAY_I_H */ diff --git a/src/pae/ieee802_1x_key.c b/src/pae/ieee802_1x_key.c index 9a8d923d14f18..d63ca7f7038ac 100644 --- a/src/pae/ieee802_1x_key.c +++ b/src/pae/ieee802_1x_key.c @@ -31,8 +31,9 @@ static void joint_two_mac(const u8 *mac1, const u8 *mac2, u8 *out) /* IEEE Std 802.1X-2010, 6.2.1 KDF */ -static int aes_kdf_128(const u8 *kdk, const char *label, const u8 *context, - int ctx_bits, int ret_bits, u8 *ret) +static int aes_kdf(const u8 *kdk, size_t kdk_bits, + const char *label, const u8 *context, + int ctx_bits, int ret_bits, u8 *ret) { const int h = 128; const int r = 8; @@ -40,6 +41,9 @@ static int aes_kdf_128(const u8 *kdk, const char *label, const u8 *context, int lab_len, ctx_len, ret_len, buf_len; u8 *buf; + if (kdk_bits != 128 && kdk_bits != 256) + return -1; + lab_len = os_strlen(label); ctx_len = (ctx_bits + 7) / 8; ret_len = ((ret_bits & 0xffff) + 7) / 8; @@ -60,8 +64,14 @@ static int aes_kdf_128(const u8 *kdk, const char *label, const u8 *context, WPA_PUT_BE16(&buf[buf_len - 2], ret_bits); for (i = 0; i < n; i++) { + int res; + buf[0] = (u8) (i + 1); - if (omac1_aes_128(kdk, buf, buf_len, ret)) { + if (kdk_bits == 128) + res = omac1_aes_128(kdk, buf, buf_len, ret); + else + res = omac1_aes_256(kdk, buf, buf_len, ret); + if (res) { os_free(buf); return -1; } @@ -72,33 +82,32 @@ static int aes_kdf_128(const u8 *kdk, const char *label, const u8 *context, } -/********** AES-CMAC-128 **********/ /** - * ieee802_1x_cak_128bits_aes_cmac + * ieee802_1x_cak_aes_cmac * * IEEE Std 802.1X-2010, 6.2.2 * CAK = KDF(Key, Label, mac1 | mac2, CAKlength) */ -int ieee802_1x_cak_128bits_aes_cmac(const u8 *msk, const u8 *mac1, - const u8 *mac2, u8 *cak) +int ieee802_1x_cak_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1, + const u8 *mac2, u8 *cak, size_t cak_bytes) { u8 context[2 * ETH_ALEN]; joint_two_mac(mac1, mac2, context); - return aes_kdf_128(msk, "IEEE8021 EAP CAK", - context, sizeof(context) * 8, 128, cak); + return aes_kdf(msk, 8 * msk_bytes, "IEEE8021 EAP CAK", + context, sizeof(context) * 8, 8 * cak_bytes, cak); } /** - * ieee802_1x_ckn_128bits_aes_cmac + * ieee802_1x_ckn_aes_cmac * * IEEE Std 802.1X-2010, 6.2.2 * CKN = KDF(Key, Label, ID | mac1 | mac2, CKNlength) */ -int ieee802_1x_ckn_128bits_aes_cmac(const u8 *msk, const u8 *mac1, - const u8 *mac2, const u8 *sid, - size_t sid_bytes, u8 *ckn) +int ieee802_1x_ckn_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1, + const u8 *mac2, const u8 *sid, + size_t sid_bytes, u8 *ckn) { int res; u8 *context; @@ -112,21 +121,21 @@ int ieee802_1x_ckn_128bits_aes_cmac(const u8 *msk, const u8 *mac1, os_memcpy(context, sid, sid_bytes); joint_two_mac(mac1, mac2, context + sid_bytes); - res = aes_kdf_128(msk, "IEEE8021 EAP CKN", context, ctx_len * 8, - 128, ckn); + res = aes_kdf(msk, 8 * msk_bytes, "IEEE8021 EAP CKN", + context, ctx_len * 8, 128, ckn); os_free(context); return res; } /** - * ieee802_1x_kek_128bits_aes_cmac + * ieee802_1x_kek_aes_cmac * * IEEE Std 802.1X-2010, 9.3.3 * KEK = KDF(Key, Label, Keyid, KEKLength) */ -int ieee802_1x_kek_128bits_aes_cmac(const u8 *cak, const u8 *ckn, - size_t ckn_bytes, u8 *kek) +int ieee802_1x_kek_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn, + size_t ckn_bytes, u8 *kek, size_t kek_bytes) { u8 context[16]; @@ -134,19 +143,20 @@ int ieee802_1x_kek_128bits_aes_cmac(const u8 *cak, const u8 *ckn, os_memset(context, 0, sizeof(context)); os_memcpy(context, ckn, (ckn_bytes < 16) ? ckn_bytes : 16); - return aes_kdf_128(cak, "IEEE8021 KEK", context, sizeof(context) * 8, - 128, kek); + return aes_kdf(cak, 8 * cak_bytes, "IEEE8021 KEK", + context, sizeof(context) * 8, + 8 * kek_bytes, kek); } /** - * ieee802_1x_ick_128bits_aes_cmac + * ieee802_1x_ick_aes_cmac * * IEEE Std 802.1X-2010, 9.3.3 * ICK = KDF(Key, Label, Keyid, ICKLength) */ -int ieee802_1x_ick_128bits_aes_cmac(const u8 *cak, const u8 *ckn, - size_t ckn_bytes, u8 *ick) +int ieee802_1x_ick_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn, + size_t ckn_bytes, u8 *ick, size_t ick_bytes) { u8 context[16]; @@ -154,22 +164,32 @@ int ieee802_1x_ick_128bits_aes_cmac(const u8 *cak, const u8 *ckn, os_memset(context, 0, sizeof(context)); os_memcpy(context, ckn, (ckn_bytes < 16) ? ckn_bytes : 16); - return aes_kdf_128(cak, "IEEE8021 ICK", context, sizeof(context) * 8, - 128, ick); + return aes_kdf(cak, 8 *cak_bytes, "IEEE8021 ICK", + context, sizeof(context) * 8, + 8 * ick_bytes, ick); } /** - * ieee802_1x_icv_128bits_aes_cmac + * ieee802_1x_icv_aes_cmac * * IEEE Std 802.1X-2010, 9.4.1 * ICV = AES-CMAC(ICK, M, 128) */ -int ieee802_1x_icv_128bits_aes_cmac(const u8 *ick, const u8 *msg, - size_t msg_bytes, u8 *icv) +int ieee802_1x_icv_aes_cmac(const u8 *ick, size_t ick_bytes, const u8 *msg, + size_t msg_bytes, u8 *icv) { - if (omac1_aes_128(ick, msg, msg_bytes, icv)) { - wpa_printf(MSG_ERROR, "MKA: omac1_aes_128 failed"); + int res; + + if (ick_bytes == 16) + res = omac1_aes_128(ick, msg, msg_bytes, icv); + else if (ick_bytes == 32) + res = omac1_aes_256(ick, msg, msg_bytes, icv); + else + return -1; + if (res) { + wpa_printf(MSG_ERROR, + "MKA: AES-CMAC failed for ICV calculation"); return -1; } return 0; @@ -177,13 +197,14 @@ int ieee802_1x_icv_128bits_aes_cmac(const u8 *ick, const u8 *msg, /** - * ieee802_1x_sak_128bits_aes_cmac + * ieee802_1x_sak_aes_cmac * * IEEE Std 802.1X-2010, 9.8.1 * SAK = KDF(Key, Label, KS-nonce | MI-value list | KN, SAKLength) */ -int ieee802_1x_sak_128bits_aes_cmac(const u8 *cak, const u8 *ctx, - size_t ctx_bytes, u8 *sak) +int ieee802_1x_sak_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ctx, + size_t ctx_bytes, u8 *sak, size_t sak_bytes) { - return aes_kdf_128(cak, "IEEE8021 SAK", ctx, ctx_bytes * 8, 128, sak); + return aes_kdf(cak, cak_bytes * 8, "IEEE8021 SAK", ctx, ctx_bytes * 8, + sak_bytes * 8, sak); } diff --git a/src/pae/ieee802_1x_key.h b/src/pae/ieee802_1x_key.h index ea318ea4dde3f..1f9058de5cccf 100644 --- a/src/pae/ieee802_1x_key.h +++ b/src/pae/ieee802_1x_key.h @@ -9,18 +9,18 @@ #ifndef IEEE802_1X_KEY_H #define IEEE802_1X_KEY_H -int ieee802_1x_cak_128bits_aes_cmac(const u8 *msk, const u8 *mac1, - const u8 *mac2, u8 *cak); -int ieee802_1x_ckn_128bits_aes_cmac(const u8 *msk, const u8 *mac1, - const u8 *mac2, const u8 *sid, - size_t sid_bytes, u8 *ckn); -int ieee802_1x_kek_128bits_aes_cmac(const u8 *cak, const u8 *ckn, - size_t ckn_bytes, u8 *kek); -int ieee802_1x_ick_128bits_aes_cmac(const u8 *cak, const u8 *ckn, - size_t ckn_bytes, u8 *ick); -int ieee802_1x_icv_128bits_aes_cmac(const u8 *ick, const u8 *msg, - size_t msg_bytes, u8 *icv); -int ieee802_1x_sak_128bits_aes_cmac(const u8 *cak, const u8 *ctx, - size_t ctx_bytes, u8 *sak); +int ieee802_1x_cak_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1, + const u8 *mac2, u8 *cak, size_t cak_bytes); +int ieee802_1x_ckn_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1, + const u8 *mac2, const u8 *sid, + size_t sid_bytes, u8 *ckn); +int ieee802_1x_kek_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn, + size_t ckn_bytes, u8 *kek, size_t kek_bytes); +int ieee802_1x_ick_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn, + size_t ckn_bytes, u8 *ick, size_t ick_bytes); +int ieee802_1x_icv_aes_cmac(const u8 *ick, size_t ick_bytes, const u8 *msg, + size_t msg_bytes, u8 *icv); +int ieee802_1x_sak_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ctx, + size_t ctx_bytes, u8 *sak, size_t sak_bytes); #endif /* IEEE802_1X_KEY_H */ diff --git a/src/pae/ieee802_1x_secy_ops.c b/src/pae/ieee802_1x_secy_ops.c index ab5339bb20462..84ee42b05896c 100644 --- a/src/pae/ieee802_1x_secy_ops.c +++ b/src/pae/ieee802_1x_secy_ops.c @@ -187,7 +187,7 @@ int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay, ops = kay->ctx; if (!ops || !ops->get_transmit_next_pn) { wpa_printf(MSG_ERROR, - "KaY: secy get_receive_lowest_pn operation not supported"); + "KaY: secy get_transmit_next_pn operation not supported"); return -1; } @@ -208,7 +208,7 @@ int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay, ops = kay->ctx; if (!ops || !ops->set_transmit_next_pn) { wpa_printf(MSG_ERROR, - "KaY: secy get_receive_lowest_pn operation not supported"); + "KaY: secy set_transmit_next_pn operation not supported"); return -1; } @@ -216,6 +216,27 @@ int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay, } +int secy_set_receive_lowest_pn(struct ieee802_1x_kay *kay, + struct receive_sa *rxsa) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay || !rxsa) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->set_receive_lowest_pn) { + wpa_printf(MSG_ERROR, + "KaY: secy set_receive_lowest_pn operation not supported"); + return -1; + } + + return ops->set_receive_lowest_pn(ops->ctx, rxsa); +} + + int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc) { struct ieee802_1x_kay_ctx *ops; diff --git a/src/pae/ieee802_1x_secy_ops.h b/src/pae/ieee802_1x_secy_ops.h index 9fb29c3ddfa01..2d112ba7c5d58 100644 --- a/src/pae/ieee802_1x_secy_ops.h +++ b/src/pae/ieee802_1x_secy_ops.h @@ -36,6 +36,8 @@ int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay, struct transmit_sa *txsa); int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay, struct transmit_sa *txsa); +int secy_set_receive_lowest_pn(struct ieee802_1x_kay *kay, + struct receive_sa *txsa); int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc); int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc); int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa); diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c index a87ee745eb284..a3db4048c4a7a 100644 --- a/src/radius/radius_client.c +++ b/src/radius/radius_client.c @@ -26,12 +26,12 @@ #define RADIUS_CLIENT_MAX_WAIT 120 /** - * RADIUS_CLIENT_MAX_RETRIES - RADIUS client maximum retries + * RADIUS_CLIENT_MAX_FAILOVER - RADIUS client maximum retries * - * Maximum number of retransmit attempts before the entry is removed from + * Maximum number of server failovers before the entry is removed from * retransmit list. */ -#define RADIUS_CLIENT_MAX_RETRIES 10 +#define RADIUS_CLIENT_MAX_FAILOVER 3 /** * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages @@ -110,11 +110,16 @@ struct radius_msg_list { os_time_t next_try; /** - * attempts - Number of transmission attempts + * attempts - Number of transmission attempts for one server */ int attempts; /** + * accu_attempts - Number of accumulated attempts + */ + int accu_attempts; + + /** * next_wait - Next retransmission wait time in seconds */ int next_wait; @@ -367,9 +372,11 @@ static int radius_client_retransmit(struct radius_client_data *radius, size_t prev_num_msgs; u8 *acct_delay_time; size_t acct_delay_time_len; + int num_servers; if (entry->msg_type == RADIUS_ACCT || entry->msg_type == RADIUS_ACCT_INTERIM) { + num_servers = conf->num_acct_servers; if (radius->acct_sock < 0) radius_client_init_acct(radius); if (radius->acct_sock < 0 && conf->num_acct_servers > 1) { @@ -386,6 +393,7 @@ static int radius_client_retransmit(struct radius_client_data *radius, conf->acct_server->retransmissions++; } } else { + num_servers = conf->num_auth_servers; if (radius->auth_sock < 0) radius_client_init_auth(radius); if (radius->auth_sock < 0 && conf->num_auth_servers > 1) { @@ -449,7 +457,15 @@ static int radius_client_retransmit(struct radius_client_data *radius, } /* retransmit; remove entry if too many attempts */ + if (entry->accu_attempts > RADIUS_CLIENT_MAX_FAILOVER * + RADIUS_CLIENT_NUM_FAILOVER * num_servers) { + wpa_printf(MSG_INFO, + "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts"); + return 1; + } + entry->attempts++; + entry->accu_attempts++; hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", radius_msg_get_hdr(entry->msg)->identifier); @@ -466,10 +482,6 @@ static int radius_client_retransmit(struct radius_client_data *radius, entry->next_wait *= 2; if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) entry->next_wait = RADIUS_CLIENT_MAX_WAIT; - if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) { - wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts"); - return 1; - } return 0; } @@ -490,6 +502,30 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) return; os_get_reltime(&now); + + while (entry) { + if (now.sec >= entry->next_try) { + s = entry->msg_type == RADIUS_AUTH ? radius->auth_sock : + radius->acct_sock; + if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER || + (s < 0 && entry->attempts > 0)) { + if (entry->msg_type == RADIUS_ACCT || + entry->msg_type == RADIUS_ACCT_INTERIM) + acct_failover++; + else + auth_failover++; + } + } + entry = entry->next; + } + + if (auth_failover) + radius_client_auth_failover(radius); + + if (acct_failover) + radius_client_acct_failover(radius); + + entry = radius->msgs; first = 0; prev = NULL; @@ -517,17 +553,6 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) continue; } - s = entry->msg_type == RADIUS_AUTH ? radius->auth_sock : - radius->acct_sock; - if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER || - (s < 0 && entry->attempts > 0)) { - if (entry->msg_type == RADIUS_ACCT || - entry->msg_type == RADIUS_ACCT_INTERIM) - acct_failover++; - else - auth_failover++; - } - if (first == 0 || entry->next_try < first) first = entry->next_try; @@ -538,6 +563,7 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) if (radius->msgs) { if (first < now.sec) first = now.sec; + eloop_cancel_timeout(radius_client_timer, radius, NULL); eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius, NULL); hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, @@ -545,12 +571,6 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) "retransmit in %ld seconds", (long int) (first - now.sec)); } - - if (auth_failover) - radius_client_auth_failover(radius); - - if (acct_failover) - radius_client_acct_failover(radius); } @@ -674,7 +694,10 @@ static void radius_client_list_add(struct radius_client_data *radius, entry->first_try = entry->last_attempt.sec; entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; entry->attempts = 1; + entry->accu_attempts = 1; entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; + if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) + entry->next_wait = RADIUS_CLIENT_MAX_WAIT; entry->next = radius->msgs; radius->msgs = entry; radius_client_update_timeout(radius); @@ -713,9 +736,9 @@ static void radius_client_list_add(struct radius_client_data *radius, * * The message is added on the retransmission queue and will be retransmitted * automatically until a response is received or maximum number of retries - * (RADIUS_CLIENT_MAX_RETRIES) is reached. No such retries are used with - * RADIUS_ACCT_INTERIM, i.e., such a pending message is removed from the queue - * automatically on transmission failure. + * (RADIUS_CLIENT_MAX_FAILOVER * RADIUS_CLIENT_NUM_FAILOVER) is reached. No + * such retries are used with RADIUS_ACCT_INTERIM, i.e., such a pending message + * is removed from the queue automatically on transmission failure. * * The related device MAC address can be used to identify pending messages that * can be removed with radius_client_flush_auth(). @@ -1087,14 +1110,13 @@ radius_change_server(struct radius_client_data *radius, } } - /* Reset retry counters for the new server */ - for (entry = radius->msgs; oserv && oserv != nserv && entry; - entry = entry->next) { + /* Reset retry counters */ + for (entry = radius->msgs; oserv && entry; entry = entry->next) { if ((auth && entry->msg_type != RADIUS_AUTH) || (!auth && entry->msg_type != RADIUS_ACCT)) continue; entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; - entry->attempts = 0; + entry->attempts = 1; entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; } diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index e3afc0d532981..b621ada554ee9 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -1,6 +1,6 @@ /* * RADIUS authentication server - * Copyright (c) 2005-2009, 2011-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2009, 2011-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -357,6 +357,7 @@ struct radius_server_data { char *subscr_remediation_url; u8 subscr_remediation_method; + char *hs20_sim_provisioning_url; char *t_c_server_url; @@ -380,6 +381,44 @@ static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx); static void radius_server_session_remove_timeout(void *eloop_ctx, void *timeout_ctx); +#ifdef CONFIG_SQLITE +#ifdef CONFIG_HS20 + +static int db_table_exists(sqlite3 *db, const char *name) +{ + char cmd[128]; + + os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name); + return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK; +} + + +static int db_table_create_sim_provisioning(sqlite3 *db) +{ + char *err = NULL; + const char *sql = + "CREATE TABLE sim_provisioning(" + " mobile_identifier_hash TEXT PRIMARY KEY," + " imsi TEXT," + " mac_addr TEXT," + " eap_method TEXT," + " timestamp TEXT" + ");"; + + RADIUS_DEBUG("Adding database table for SIM provisioning information"); + if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { + RADIUS_ERROR("SQLite error: %s", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + +#endif /* CONFIG_HS20 */ +#endif /* CONFIG_SQLITE */ + + void srv_log(struct radius_session *sess, const char *fmt, ...) PRINTF_FORMAT(2, 3); @@ -637,6 +676,23 @@ static void radius_server_testing_options(struct radius_session *sess, } +#ifdef CONFIG_ERP +static struct eap_server_erp_key * +radius_server_erp_find_key(struct radius_server_data *data, const char *keyname) +{ + struct eap_server_erp_key *erp; + + dl_list_for_each(erp, &data->erp_keys, struct eap_server_erp_key, + list) { + if (os_strcmp(erp->keyname_nai, keyname) == 0) + return erp; + } + + return NULL; +} +#endif /* CONFIG_ERP */ + + static struct radius_session * radius_server_get_new_session(struct radius_server_data *data, struct radius_client *client, @@ -647,7 +703,7 @@ radius_server_get_new_session(struct radius_server_data *data, int res; struct radius_session *sess; struct eap_config eap_conf; - struct eap_user tmp; + struct eap_user *tmp; RADIUS_DEBUG("Creating a new session"); @@ -658,12 +714,27 @@ radius_server_get_new_session(struct radius_server_data *data, } RADIUS_DUMP_ASCII("User-Name", user, user_len); - os_memset(&tmp, 0, sizeof(tmp)); - res = data->get_eap_user(data->conf_ctx, user, user_len, 0, &tmp); - bin_clear_free(tmp.password, tmp.password_len); + tmp = os_zalloc(sizeof(*tmp)); + if (!tmp) + return NULL; + res = data->get_eap_user(data->conf_ctx, user, user_len, 0, tmp); +#ifdef CONFIG_ERP + if (res != 0 && data->erp) { + char *username; + + username = os_zalloc(user_len + 1); + if (username) { + os_memcpy(username, user, user_len); + if (radius_server_erp_find_key(data, username)) + res = 0; + os_free(username); + } + } +#endif /* CONFIG_ERP */ if (res != 0) { RADIUS_DEBUG("User-Name not found from user database"); + eap_user_free(tmp); return NULL; } @@ -671,10 +742,12 @@ radius_server_get_new_session(struct radius_server_data *data, sess = radius_server_new_session(data, client); if (sess == NULL) { RADIUS_DEBUG("Failed to create a new session"); + eap_user_free(tmp); return NULL; } - sess->accept_attr = tmp.accept_attr; - sess->macacl = tmp.macacl; + sess->accept_attr = tmp->accept_attr; + sess->macacl = tmp->macacl; + eap_user_free(tmp); sess->username = os_malloc(user_len * 4 + 1); if (sess->username == NULL) { @@ -866,6 +939,117 @@ static void db_update_last_msk(struct radius_session *sess, const char *msk) } +#ifdef CONFIG_HS20 + +static int radius_server_is_sim_method(struct radius_session *sess) +{ + const char *name; + + name = eap_get_method(sess->eap); + return name && + (os_strcmp(name, "SIM") == 0 || + os_strcmp(name, "AKA") == 0 || + os_strcmp(name, "AKA'") == 0); +} + + +static int radius_server_hs20_missing_sim_pps(struct radius_msg *request) +{ + u8 *buf, *pos, *end, type, sublen; + size_t len; + + buf = NULL; + for (;;) { + if (radius_msg_get_attr_ptr(request, + RADIUS_ATTR_VENDOR_SPECIFIC, + &buf, &len, buf) < 0) + return 0; + if (len < 6) + continue; + pos = buf; + end = buf + len; + if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA) + continue; + pos += 4; + + type = *pos++; + sublen = *pos++; + if (sublen < 2) + continue; /* invalid length */ + sublen -= 2; /* skip header */ + if (pos + sublen > end) + continue; /* invalid WFA VSA */ + + if (type != RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION) + continue; + + RADIUS_DUMP("HS2.0 mobile device version", pos, sublen); + if (sublen < 1 + 2) + continue; + if (pos[0] == 0) + continue; /* Release 1 STA does not support provisioning + + */ + /* UpdateIdentifier 0 indicates no PPS MO */ + return WPA_GET_BE16(pos + 1) == 0; + } +} + + +#define HS20_MOBILE_ID_HASH_LEN 16 + +static int radius_server_sim_provisioning_session(struct radius_session *sess, + const u8 *hash) +{ +#ifdef CONFIG_SQLITE + char *sql; + char addr_txt[ETH_ALEN * 3]; + char hash_txt[2 * HS20_MOBILE_ID_HASH_LEN + 1]; + struct os_time now; + int res; + const char *imsi, *eap_method; + + if (!sess->server->db || + (!db_table_exists(sess->server->db, "sim_provisioning") && + db_table_create_sim_provisioning(sess->server->db) < 0)) + return -1; + + imsi = eap_get_imsi(sess->eap); + if (!imsi) + return -1; + + eap_method = eap_get_method(sess->eap); + if (!eap_method) + return -1; + + os_snprintf(addr_txt, sizeof(addr_txt), MACSTR, + MAC2STR(sess->mac_addr)); + wpa_snprintf_hex(hash_txt, sizeof(hash_txt), hash, + HS20_MOBILE_ID_HASH_LEN); + + os_get_time(&now); + sql = sqlite3_mprintf("INSERT INTO sim_provisioning(mobile_identifier_hash,imsi,mac_addr,eap_method,timestamp) VALUES (%Q,%Q,%Q,%Q,%u)", + hash_txt, imsi, addr_txt, eap_method, now.sec); + if (!sql) + return -1; + + if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) != + SQLITE_OK) { + RADIUS_ERROR("Failed to add SIM provisioning entry into sqlite database: %s", + sqlite3_errmsg(sess->server->db)); + res = -1; + } else { + res = 0; + } + sqlite3_free(sql); + return res; +#endif /* CONFIG_SQLITE */ + return -1; +} + +#endif /* CONFIG_HS20 */ + + static struct radius_msg * radius_server_encapsulate_eap(struct radius_server_data *data, struct radius_client *client, @@ -979,6 +1163,48 @@ radius_server_encapsulate_eap(struct radius_server_data *data, buf, 0)) { RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem"); } + } else if (code == RADIUS_CODE_ACCESS_ACCEPT && + data->hs20_sim_provisioning_url && + radius_server_is_sim_method(sess) && + radius_server_hs20_missing_sim_pps(request)) { + u8 *buf, *pos, hash[HS20_MOBILE_ID_HASH_LEN]; + size_t prefix_len, url_len; + + RADIUS_DEBUG("Device needs HS 2.0 SIM provisioning"); + + if (os_get_random(hash, HS20_MOBILE_ID_HASH_LEN) < 0) { + radius_msg_free(msg); + return NULL; + } + RADIUS_DUMP("hotspot2dot0-mobile-identifier-hash", + hash, HS20_MOBILE_ID_HASH_LEN); + + if (radius_server_sim_provisioning_session(sess, hash) < 0) { + radius_msg_free(msg); + return NULL; + } + + prefix_len = os_strlen(data->hs20_sim_provisioning_url); + url_len = prefix_len + 2 * HS20_MOBILE_ID_HASH_LEN; + buf = os_malloc(1 + url_len + 1); + if (!buf) { + radius_msg_free(msg); + return NULL; + } + pos = buf; + *pos++ = data->subscr_remediation_method; + os_memcpy(pos, data->hs20_sim_provisioning_url, prefix_len); + pos += prefix_len; + wpa_snprintf_hex((char *) pos, 2 * HS20_MOBILE_ID_HASH_LEN + 1, + hash, HS20_MOBILE_ID_HASH_LEN); + RADIUS_DEBUG("HS 2.0 subscription remediation URL: %s", + (char *) &buf[1]); + if (!radius_msg_add_wfa( + msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION, + buf, 1 + url_len)) { + RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem"); + } + os_free(buf); } if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->t_c_filtering) { @@ -2059,7 +2285,7 @@ radius_server_read_clients(const char *client_file, int ipv6) entry->addr.s_addr = addr.s_addr; val = 0; for (i = 0; i < mask; i++) - val |= 1 << (31 - i); + val |= 1U << (31 - i); entry->mask.s_addr = htonl(val); } #ifdef CONFIG_IPV6 @@ -2173,6 +2399,9 @@ radius_server_init(struct radius_server_conf *conf) os_strdup(conf->subscr_remediation_url); } data->subscr_remediation_method = conf->subscr_remediation_method; + if (conf->hs20_sim_provisioning_url) + data->hs20_sim_provisioning_url = + os_strdup(conf->hs20_sim_provisioning_url); if (conf->t_c_server_url) data->t_c_server_url = os_strdup(conf->t_c_server_url); @@ -2293,6 +2522,7 @@ void radius_server_deinit(struct radius_server_data *data) os_free(data->dump_msk_file); #endif /* CONFIG_RADIUS_TEST */ os_free(data->subscr_remediation_url); + os_free(data->hs20_sim_provisioning_url); os_free(data->t_c_server_url); #ifdef CONFIG_SQLITE @@ -2506,15 +2736,8 @@ radius_server_erp_get_key(void *ctx, const char *keyname) { struct radius_session *sess = ctx; struct radius_server_data *data = sess->server; - struct eap_server_erp_key *erp; - - dl_list_for_each(erp, &data->erp_keys, struct eap_server_erp_key, - list) { - if (os_strcmp(erp->keyname_nai, keyname) == 0) - return erp; - } - return NULL; + return radius_server_erp_find_key(data, keyname); } diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h index 167bbf5b28813..53728f9d7bf27 100644 --- a/src/radius/radius_server.h +++ b/src/radius/radius_server.h @@ -233,6 +233,7 @@ struct radius_server_conf { char *subscr_remediation_url; u8 subscr_remediation_method; + char *hs20_sim_provisioning_url; char *t_c_server_url; }; diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c index fdd522087dbc9..d720f7b811441 100644 --- a/src/rsn_supp/pmksa_cache.c +++ b/src/rsn_supp/pmksa_cache.c @@ -263,7 +263,8 @@ pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, } pmksa->pmksa_count++; wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR - " network_ctx=%p", MAC2STR(entry->aa), entry->network_ctx); + " network_ctx=%p akmp=0x%x", MAC2STR(entry->aa), + entry->network_ctx, entry->akmp); wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid, entry->fils_cache_id_set ? entry->fils_cache_id : NULL, entry->pmk, entry->pmk_len); diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c index 345b0c84d871c..704c95e686169 100644 --- a/src/rsn_supp/tdls.c +++ b/src/rsn_supp/tdls.c @@ -1594,7 +1594,7 @@ static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde, peer->supp_rates, sizeof(peer->supp_rates), kde->supp_rates + 2, kde->supp_rates_len - 2, kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL, - kde->ext_supp_rates_len - 2); + kde->ext_supp_rates ? kde->ext_supp_rates_len - 2 : 0); return 0; } diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index e0c913074c631..9163f61fa2f24 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -20,8 +20,10 @@ #include "crypto/sha512.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "common/ocv.h" #include "eap_common/eap_defs.h" #include "eapol_supp/eapol_supp_sm.h" +#include "drivers/driver.h" #include "wpa.h" #include "eloop.h" #include "preauth.h" @@ -382,6 +384,11 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, if (!sm->cur_pmksa) sm->cur_pmksa = sa; +#ifdef CONFIG_IEEE80211R + } else if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->ft_protocol) { + wpa_printf(MSG_DEBUG, + "FT: Continue 4-way handshake without PMK/PMKID for association using FT protocol"); +#endif /* CONFIG_IEEE80211R */ } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get master session key from " @@ -532,15 +539,25 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk) { + const u8 *z = NULL; + size_t z_len = 0; + #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) return wpa_derive_ptk_ft(sm, src_addr, key, ptk); #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_DPP2 + if (sm->key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) { + z = wpabuf_head(sm->dpp_z); + z_len = wpabuf_len(sm->dpp_z); + } +#endif /* CONFIG_DPP2 */ + return wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion", sm->own_addr, sm->bssid, sm->snonce, key->key_nonce, ptk, sm->key_mgmt, - sm->pairwise_cipher); + sm->pairwise_cipher, z, z_len); } @@ -618,6 +635,33 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, kde = sm->assoc_wpa_ie; kde_len = sm->assoc_wpa_ie_len; +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(sm)) { + struct wpa_channel_info ci; + u8 *pos; + + if (wpa_sm_channel_info(sm, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element in EAPOL-Key 2/4"); + goto failed; + } + + kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 3); + if (!kde_buf) { + wpa_printf(MSG_WARNING, + "Failed to allocate memory for KDE with OCI in EAPOL-Key 2/4"); + goto failed; + } + + os_memcpy(kde_buf, kde, kde_len); + kde = kde_buf; + pos = kde + kde_len; + if (ocv_insert_oci_kde(&ci, &pos) < 0) + goto failed; + kde_len = pos - kde; + } +#endif /* CONFIG_OCV */ + #ifdef CONFIG_P2P if (sm->p2p) { kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 1); @@ -686,7 +730,9 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, * likelihood of the first preauth EAPOL-Start frame getting to * the target AP. */ - eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL); + if (!dl_list_empty(&sm->pmksa_candidates)) + eloop_register_timeout(1, 0, wpa_sm_start_preauth, + sm, NULL); } if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) { @@ -980,8 +1026,6 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, } os_memset(&gd, 0, sizeof(gd)); - wpa_supplicant_key_neg_complete(sm, sm->bssid, - key_info & WPA_KEY_INFO_SECURE); return 0; } @@ -1019,9 +1063,27 @@ static int wpa_supplicant_install_igtk(struct wpa_sm *sm, broadcast_ether_addr, keyidx, 0, igtk->pn, sizeof(igtk->pn), igtk->igtk, len) < 0) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "WPA: Failed to configure IGTK to the driver"); - return -1; + if (keyidx == 0x0400 || keyidx == 0x0500) { + /* Assume the AP has broken PMF implementation since it + * seems to have swapped the KeyID bytes. The AP cannot + * be trusted to implement BIP correctly or provide a + * valid IGTK, so do not try to configure this key with + * swapped KeyID bytes. Instead, continue without + * configuring the IGTK so that the driver can drop any + * received group-addressed robust management frames due + * to missing keys. + * + * Normally, this error behavior would result in us + * disconnecting, but there are number of deployed APs + * with this broken behavior, so as an interoperability + * workaround, allow the connection to proceed. */ + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Ignore IGTK configuration error due to invalid IGTK KeyID byte order"); + } else { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to configure IGTK to the driver"); + return -1; + } } if (wnm_sleep) { @@ -1418,6 +1480,26 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, } #endif /* CONFIG_P2P */ +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(sm)) { + struct wpa_channel_info ci; + + if (wpa_sm_channel_info(sm, &ci) != 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "Failed to get channel info to validate received OCI in EAPOL-Key 3/4"); + return; + } + + if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx) != 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "%s", + ocv_errorstr); + return; + } + } +#endif /* CONFIG_OCV */ + if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, &sm->ptk) < 0) { goto failed; @@ -1442,8 +1524,11 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) { - wpa_supplicant_key_neg_complete(sm, sm->bssid, - key_info & WPA_KEY_INFO_SECURE); + /* No GTK to be set to the driver */ + } else if (!ie.gtk && sm->proto == WPA_PROTO_RSN) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: No GTK KDE included in EAPOL-Key msg 3/4"); + goto failed; } else if (ie.gtk && wpa_supplicant_pairwise_gtk(sm, key, ie.gtk, ie.gtk_len, key_info) < 0) { @@ -1458,6 +1543,10 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, goto failed; } + if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED || ie.gtk) + wpa_supplicant_key_neg_complete(sm, sm->bssid, + key_info & WPA_KEY_INFO_SECURE); + if (ie.gtk) wpa_sm_set_rekey_offload(sm); @@ -1511,6 +1600,26 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, } maxkeylen = gd->gtk_len = ie.gtk_len - 2; +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(sm)) { + struct wpa_channel_info ci; + + if (wpa_sm_channel_info(sm, &ci) != 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "Failed to get channel info to validate received OCI in EAPOL-Key group msg 1/2"); + return -1; + } + + if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx) != 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "%s", + ocv_errorstr); + return -1; + } + } +#endif /* CONFIG_OCV */ + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gd->gtk_len, maxkeylen, &gd->key_rsc_len, &gd->alg)) @@ -1631,11 +1740,17 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; u8 *rbuf, *key_mic; + size_t kde_len = 0; + +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(sm)) + kde_len = OCV_OCI_KDE_LEN; +#endif /* CONFIG_OCV */ mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); hdrlen = sizeof(*reply) + mic_len + 2; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, - hdrlen, &rlen, (void *) &reply); + hdrlen + kde_len, &rlen, (void *) &reply); if (rbuf == NULL) return -1; @@ -1657,7 +1772,27 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, WPA_REPLAY_COUNTER_LEN); key_mic = (u8 *) (reply + 1); - WPA_PUT_BE16(key_mic + mic_len, 0); + WPA_PUT_BE16(key_mic + mic_len, kde_len); /* Key Data Length */ + +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(sm)) { + struct wpa_channel_info ci; + u8 *pos; + + if (wpa_sm_channel_info(sm, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element in EAPOL-Key 2/2"); + os_free(rbuf); + return -1; + } + + pos = key_mic + mic_len + 2; /* Key Data */ + if (ocv_insert_oci_kde(&ci, &pos) < 0) { + os_free(rbuf); + return -1; + } + } +#endif /* CONFIG_OCV */ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); return wpa_eapol_key_send(sm, &sm->ptk, ver, sm->bssid, ETH_P_EAPOL, @@ -1755,7 +1890,15 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid EAPOL-Key MIC " "when using TPTK - ignoring TPTK"); +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore Key MIC failure for fuzz testing"); + goto continue_fuzz; +#endif /* TEST_FUZZ */ } else { +#ifdef TEST_FUZZ + continue_fuzz: +#endif /* TEST_FUZZ */ ok = 1; sm->tptk_set = 0; sm->ptk_set = 1; @@ -1781,8 +1924,16 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid EAPOL-Key MIC - " "dropping packet"); +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore Key MIC failure for fuzz testing"); + goto continue_fuzz2; +#endif /* TEST_FUZZ */ return -1; } +#ifdef TEST_FUZZ + continue_fuzz2: +#endif /* TEST_FUZZ */ ok = 1; } @@ -1857,14 +2008,25 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, "WPA: No memory for AES-UNWRAP buffer"); return -1; } +#ifdef TEST_FUZZ + os_memset(buf, 0x11, *key_data_len); +#endif /* TEST_FUZZ */ if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, *key_data_len / 8, key_data, buf)) { +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore AES unwrap failure for fuzz testing"); + goto continue_fuzz; +#endif /* TEST_FUZZ */ bin_clear_free(buf, *key_data_len); wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: AES unwrap failed - " "could not decrypt EAPOL-Key key data"); return -1; } +#ifdef TEST_FUZZ + continue_fuzz: +#endif /* TEST_FUZZ */ os_memcpy(key_data, buf, *key_data_len); bin_clear_free(buf, *key_data_len); WPA_PUT_BE16(((u8 *) (key + 1)) + mic_len, *key_data_len); @@ -2513,6 +2675,9 @@ void wpa_sm_deinit(struct wpa_sm *sm) #ifdef CONFIG_OWE crypto_ecdh_deinit(sm->owe_ecdh); #endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP2 + wpabuf_clear_free(sm->dpp_z); +#endif /* CONFIG_DPP2 */ os_free(sm); } @@ -2554,6 +2719,9 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) wpa_ft_prepare_auth_request(sm, NULL); clear_keys = 0; + sm->ft_protocol = 1; + } else { + sm->ft_protocol = 0; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_FILS @@ -2618,6 +2786,7 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) #endif /* CONFIG_FILS */ #ifdef CONFIG_IEEE80211R sm->ft_reassoc_completed = 0; + sm->ft_protocol = 0; #endif /* CONFIG_IEEE80211R */ /* Keys are not needed in the WPA state machine anymore */ @@ -2864,6 +3033,9 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, case WPA_PARAM_MFP: sm->mfp = value; break; + case WPA_PARAM_OCV: + sm->ocv = value; + break; default: break; } @@ -2938,6 +3110,19 @@ int wpa_sm_pmf_enabled(struct wpa_sm *sm) } +int wpa_sm_ocv_enabled(struct wpa_sm *sm) +{ + struct wpa_ie_data rsn; + + if (!sm->ocv || !sm->ap_rsn_ie) + return 0; + + return wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, + &rsn) >= 0 && + (rsn.capabilities & WPA_CAPABILITY_OCVC); +} + + /** * wpa_sm_set_assoc_wpa_ie_default - Generate own WPA/RSN IE from configuration * @sm: Pointer to WPA state machine data from wpa_sm_init() @@ -3817,6 +4002,8 @@ static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf) if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) capab |= WPA_CAPABILITY_MFPC; #endif /* CONFIG_IEEE80211W */ + if (sm->ocv) + capab |= WPA_CAPABILITY_OCVC; wpabuf_put_le16(buf, capab); /* PMKID Count */ @@ -3846,11 +4033,13 @@ static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf) MAC2STR(sm->r1kh_id)); pos = wpabuf_put(buf, WPA_PMK_NAME_LEN); if (wpa_derive_pmk_r1_name(sm->pmk_r0_name, sm->r1kh_id, sm->own_addr, - pos, use_sha384) < 0) { + sm->pmk_r1_name, use_sha384) < 0) { wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name"); return -1; } - wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", pos, WPA_PMK_NAME_LEN); + wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name, + WPA_PMK_NAME_LEN); + os_memcpy(pos, sm->pmk_r1_name, WPA_PMK_NAME_LEN); #ifdef CONFIG_IEEE80211W if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { @@ -3951,6 +4140,26 @@ struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek, /* TODO: FILS IP Address Assignment */ +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(sm)) { + struct wpa_channel_info ci; + u8 *pos; + + if (wpa_sm_channel_info(sm, &ci) != 0) { + wpa_printf(MSG_WARNING, + "FILS: Failed to get channel info for OCI element"); + wpabuf_free(buf); + return NULL; + } + + pos = wpabuf_put(buf, OCV_OCI_EXTENDED_LEN); + if (ocv_insert_extended_oci(&ci, pos) < 0) { + wpabuf_free(buf); + return NULL; + } + } +#endif /* CONFIG_OCV */ + wpa_hexdump_buf(MSG_DEBUG, "FILS: Association Request plaintext", buf); *kek = sm->ptk.kek; @@ -4114,6 +4323,43 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len) goto fail; } +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(sm)) { + struct wpa_channel_info ci; + + if (wpa_sm_channel_info(sm, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info to validate received OCI in FILS (Re)Association Response frame"); + goto fail; + } + + if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx) != 0) { + wpa_printf(MSG_WARNING, "FILS: %s", ocv_errorstr); + goto fail; + } + } +#endif /* CONFIG_OCV */ + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->fils_ft_ies) { + struct wpa_ie_data rsn; + + /* Check that PMKR1Name derived by the AP matches */ + if (!elems.rsn_ie || + wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsn) < 0 || + !rsn.pmkid || rsn.num_pmkid != 1 || + os_memcmp(rsn.pmkid, sm->pmk_r1_name, + WPA_PMK_NAME_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "FILS+FT: No RSNE[PMKR1Name] match in AssocResp"); + goto fail; + } + } +#endif /* CONFIG_IEEE80211R */ + /* Key Delivery */ if (!elems.key_delivery) { wpa_printf(MSG_DEBUG, "FILS: No Key Delivery element"); @@ -4435,3 +4681,14 @@ void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id) } #endif /* CONFIG_FILS */ } + + +#ifdef CONFIG_DPP2 +void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z) +{ + if (sm) { + wpabuf_clear_free(sm->dpp_z); + sm->dpp_z = z ? wpabuf_dup(z) : NULL; + } +} +#endif /* CONFIG_DPP2 */ diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 21f4b17815e93..8903f8e14c691 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -18,6 +18,7 @@ struct wpa_sm; struct eapol_sm; struct wpa_config_blob; struct hostapd_freq_params; +struct wpa_channel_info; struct wpa_sm_ctx { void *ctx; /* pointer to arbitrary upper level context */ @@ -82,6 +83,7 @@ struct wpa_sm_ctx { int (*key_mgmt_set_pmk)(void *ctx, const u8 *pmk, size_t pmk_len); void (*fils_hlp_rx)(void *ctx, const u8 *dst, const u8 *src, const u8 *pkt, size_t pkt_len); + int (*channel_info)(void *ctx, struct wpa_channel_info *ci); }; @@ -95,7 +97,8 @@ enum wpa_sm_conf_params { WPA_PARAM_KEY_MGMT, WPA_PARAM_MGMT_GROUP, WPA_PARAM_RSN_ENABLED, - WPA_PARAM_MFP + WPA_PARAM_MFP, + WPA_PARAM_OCV }; struct rsn_supp_config { @@ -141,6 +144,7 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose); int wpa_sm_pmf_enabled(struct wpa_sm *sm); +int wpa_sm_ocv_enabled(struct wpa_sm *sm); void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise); @@ -279,6 +283,11 @@ static inline int wpa_sm_pmf_enabled(struct wpa_sm *sm) return 0; } +static inline int wpa_sm_ocv_enabled(struct wpa_sm *sm) +{ + return 0; +} + static inline void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) { @@ -456,5 +465,6 @@ int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid, void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set); void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id); +void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z); #endif /* WPA_H */ diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index b8d60e3208d09..7dcb1043bfcc4 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -14,6 +14,8 @@ #include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "common/ocv.h" +#include "drivers/driver.h" #include "wpa.h" #include "wpa_i.h" @@ -242,6 +244,8 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, sm->mgmt_group_cipher == WPA_CIPHER_BIP_CMAC_256) capab |= WPA_CAPABILITY_MFPC; #endif /* CONFIG_IEEE80211W */ + if (sm->ocv) + capab |= WPA_CAPABILITY_OCVC; WPA_PUT_LE16(pos, capab); pos += 2; @@ -323,6 +327,26 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, *pos++ = sm->r0kh_id_len; os_memcpy(pos, sm->r0kh_id, sm->r0kh_id_len); pos += sm->r0kh_id_len; +#ifdef CONFIG_OCV + if (kck && wpa_sm_ocv_enabled(sm)) { + /* OCI sub-element in the third FT message */ + struct wpa_channel_info ci; + + if (wpa_sm_channel_info(sm, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element in FTE"); + os_free(buf); + return NULL; + } + + *pos++ = FTIE_SUBELEM_OCI; + *pos++ = OCV_OCI_LEN; + if (ocv_insert_oci(&ci, &pos) < 0) { + os_free(buf); + return NULL; + } + } +#endif /* CONFIG_OCV */ *ftie_len = pos - ftie_len - 1; if (ric_ies) { @@ -961,6 +985,25 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return -1; } +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(sm)) { + struct wpa_channel_info ci; + + if (wpa_sm_channel_info(sm, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info to validate received OCI in (Re)Assoc Response"); + return -1; + } + + if (ocv_verify_tx_params(parse.oci, parse.oci_len, &ci, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx) != 0) { + wpa_printf(MSG_WARNING, "%s", ocv_errorstr); + return -1; + } + } +#endif /* CONFIG_OCV */ + sm->ft_reassoc_completed = 1; if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0) diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index b94b17a85a3a6..0c5955c66f882 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -86,6 +86,7 @@ struct wpa_sm { int rsn_enabled; /* Whether RSN is enabled in configuration */ int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */ + int ocv; /* Operating Channel Validation */ u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */ size_t assoc_wpa_ie_len; @@ -125,8 +126,9 @@ struct wpa_sm { u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; size_t r0kh_id_len; u8 r1kh_id[FT_R1KH_ID_LEN]; - int ft_completed; - int ft_reassoc_completed; + unsigned int ft_completed:1; + unsigned int ft_reassoc_completed:1; + unsigned int ft_protocol:1; int over_the_ds_in_progress; u8 target_ap[ETH_ALEN]; /* over-the-DS target AP */ int set_ptk_after_assoc; @@ -167,6 +169,10 @@ struct wpa_sm { struct crypto_ecdh *owe_ecdh; u16 owe_group; #endif /* CONFIG_OWE */ + +#ifdef CONFIG_DPP2 + struct wpabuf *dpp_z; +#endif /* CONFIG_DPP2 */ }; @@ -395,6 +401,14 @@ static inline void wpa_sm_fils_hlp_rx(struct wpa_sm *sm, sm->ctx->fils_hlp_rx(sm->ctx->ctx, dst, src, pkt, pkt_len); } +static inline int wpa_sm_channel_info(struct wpa_sm *sm, + struct wpa_channel_info *ci) +{ + if (!sm->ctx->channel_info) + return -1; + return sm->ctx->channel_info(sm->ctx->ctx, ci); +} + int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk, int ver, const u8 *dest, u16 proto, diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c index a3410d15447a4..ae9f4ca241d82 100644 --- a/src/rsn_supp/wpa_ie.c +++ b/src/rsn_supp/wpa_ie.c @@ -223,6 +223,8 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, if (sm->mfp == 2) capab |= WPA_CAPABILITY_MFPR; #endif /* CONFIG_IEEE80211W */ + if (sm->ocv) + capab |= WPA_CAPABILITY_OCVC; WPA_PUT_LE16(pos, capab); pos += 2; @@ -463,6 +465,17 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, } #endif /* CONFIG_P2P */ +#ifdef CONFIG_OCV + if (pos[1] >= RSN_SELECTOR_LEN + 1 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_OCI) { + ie->oci = pos + 2 + RSN_SELECTOR_LEN; + ie->oci_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: OCI KDE in EAPOL-Key", + pos, pos[1] + 2); + return 0; + } +#endif /* CONFIG_OCV */ + return 0; } diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h index 0e72af56029eb..9d53973a94314 100644 --- a/src/rsn_supp/wpa_ie.h +++ b/src/rsn_supp/wpa_ie.h @@ -53,6 +53,10 @@ struct wpa_eapol_ie_parse { const u8 *ip_addr_req; const u8 *ip_addr_alloc; #endif /* CONFIG_P2P */ +#ifdef CONFIG_OCV + const u8 *oci; + size_t oci_len; +#endif /* CONFIG_OCV */ }; int wpa_supplicant_parse_ies(const u8 *buf, size_t len, diff --git a/src/tls/asn1.c b/src/tls/asn1.c index cec109292d5ab..822f87c182124 100644 --- a/src/tls/asn1.c +++ b/src/tls/asn1.c @@ -31,6 +31,10 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) pos = buf; end = buf + len; + if (pos >= end) { + wpa_printf(MSG_DEBUG, "ASN.1: No room for Identifier"); + return -1; + } hdr->identifier = *pos++; hdr->class = hdr->identifier >> 6; hdr->constructed = !!(hdr->identifier & (1 << 5)); @@ -51,6 +55,10 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) } else hdr->tag = hdr->identifier & 0x1f; + if (pos >= end) { + wpa_printf(MSG_DEBUG, "ASN.1: No room for Length"); + return -1; + } tmp = *pos++; if (tmp & 0x80) { if (tmp == 0xff) { diff --git a/src/tls/bignum.c b/src/tls/bignum.c index f3baafe1061df..1a87c82d5a726 100644 --- a/src/tls/bignum.c +++ b/src/tls/bignum.c @@ -119,10 +119,10 @@ int bignum_cmp(const struct bignum *a, const struct bignum *b) /** - * bignum_cmd_d - Compare bignum to standard integer + * bignum_cmp_d - Compare bignum to standard integer * @a: Bignum from bignum_init() * @b: Small integer - * Returns: 0 on success, -1 on failure + * Returns: -1 if a < b, 0 if a == b, 1 if a > b */ int bignum_cmp_d(const struct bignum *a, unsigned long b) { diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c index 76e19746b0209..a147a54a3d106 100644 --- a/src/tls/tlsv1_client.c +++ b/src/tls/tlsv1_client.c @@ -1,6 +1,6 @@ /* * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -514,6 +514,8 @@ int tlsv1_client_established(struct tlsv1_client *conn) * tlsv1_client_prf - Use TLS-PRF to derive keying material * @conn: TLSv1 client connection data from tlsv1_client_init() * @label: Label (e.g., description of the key) for PRF + * @context: Optional extra upper-layer context (max len 2^16) + * @context_len: The length of the context value * @server_random_first: seed is 0 = client_random|server_random, * 1 = server_random|client_random * @out: Buffer for output data from TLS-PRF @@ -521,13 +523,26 @@ int tlsv1_client_established(struct tlsv1_client *conn) * Returns: 0 on success, -1 on failure */ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len) { - u8 seed[2 * TLS_RANDOM_LEN]; + u8 *seed, *pos; + size_t seed_len = 2 * TLS_RANDOM_LEN; + int res; if (conn->state != ESTABLISHED) return -1; + if (context_len > 65535) + return -1; + + if (context) + seed_len += 2 + context_len; + + seed = os_malloc(seed_len); + if (!seed) + return -1; + if (server_random_first) { os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, @@ -538,9 +553,18 @@ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, TLS_RANDOM_LEN); } - return tls_prf(conn->rl.tls_version, - conn->master_secret, TLS_MASTER_SECRET_LEN, - label, seed, 2 * TLS_RANDOM_LEN, out, out_len); + if (context) { + pos = seed + 2 * TLS_RANDOM_LEN; + WPA_PUT_BE16(pos, context_len); + pos += 2; + os_memcpy(pos, context, context_len); + } + + res = tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + label, seed, seed_len, out, out_len); + os_free(seed); + return res; } diff --git a/src/tls/tlsv1_client.h b/src/tls/tlsv1_client.h index 40fa6c7fbdeeb..7fcc256f14aaa 100644 --- a/src/tls/tlsv1_client.h +++ b/src/tls/tlsv1_client.h @@ -1,6 +1,6 @@ /* * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -19,6 +19,7 @@ struct tlsv1_client * tlsv1_client_init(void); void tlsv1_client_deinit(struct tlsv1_client *conn); int tlsv1_client_established(struct tlsv1_client *conn); int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len); u8 * tlsv1_client_handshake(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c index e66f1a98896de..80874e59d1de5 100644 --- a/src/tls/tlsv1_client_read.c +++ b/src/tls/tlsv1_client_read.c @@ -290,7 +290,7 @@ static void tls_peer_cert_event(struct tlsv1_client *conn, int depth, return; os_memset(&ev, 0, sizeof(ev)); - if (conn->cred->cert_probe || conn->cert_in_cb) { + if ((conn->cred && conn->cred->cert_probe) || conn->cert_in_cb) { cert_buf = wpabuf_alloc_copy(cert->cert_start, cert->cert_len); ev.peer_cert.cert = cert_buf; diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c index 04d895e61926a..4a1147b69e761 100644 --- a/src/tls/tlsv1_client_write.c +++ b/src/tls/tlsv1_client_write.c @@ -72,6 +72,9 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) *out_len = 0; os_get_time(&now); +#ifdef TEST_FUZZ + now.sec = 0xfffefdfc; +#endif /* TEST_FUZZ */ WPA_PUT_BE32(conn->client_random, now.sec); if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c index 5406969040959..12dcc859a2803 100644 --- a/src/tls/tlsv1_server.c +++ b/src/tls/tlsv1_server.c @@ -1,6 +1,6 @@ /* * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -164,7 +164,8 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn, /* need more data */ wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not " "yet supported"); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); goto failed; } ct = pos[0]; @@ -204,6 +205,7 @@ failed: msg = tlsv1_server_send_alert(conn, conn->alert_level, conn->alert_description, out_len); + conn->write_alerts++; } return msg; @@ -296,6 +298,7 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, } tlsv1_server_log(conn, "Received alert %d:%d", out_pos[0], out_pos[1]); + conn->read_alerts++; if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { /* Continue processing */ pos += used; @@ -459,6 +462,8 @@ int tlsv1_server_established(struct tlsv1_server *conn) * tlsv1_server_prf - Use TLS-PRF to derive keying material * @conn: TLSv1 server connection data from tlsv1_server_init() * @label: Label (e.g., description of the key) for PRF + * @context: Optional extra upper-layer context (max len 2^16) + * @context_len: The length of the context value * @server_random_first: seed is 0 = client_random|server_random, * 1 = server_random|client_random * @out: Buffer for output data from TLS-PRF @@ -466,13 +471,26 @@ int tlsv1_server_established(struct tlsv1_server *conn) * Returns: 0 on success, -1 on failure */ int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len) { - u8 seed[2 * TLS_RANDOM_LEN]; + u8 *seed, *pos; + size_t seed_len = 2 * TLS_RANDOM_LEN; + int res; if (conn->state != ESTABLISHED) return -1; + if (context_len > 65535) + return -1; + + if (context) + seed_len += 2 + context_len; + + seed = os_malloc(seed_len); + if (!seed) + return -1; + if (server_random_first) { os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, @@ -483,9 +501,18 @@ int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, TLS_RANDOM_LEN); } - return tls_prf(conn->rl.tls_version, - conn->master_secret, TLS_MASTER_SECRET_LEN, - label, seed, 2 * TLS_RANDOM_LEN, out, out_len); + if (context) { + pos = seed + 2 * TLS_RANDOM_LEN; + WPA_PUT_BE16(pos, context_len); + pos += 2; + os_memcpy(pos, context, context_len); + } + + res = tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + label, seed, seed_len, out, out_len); + os_free(seed); + return res; } @@ -708,6 +735,24 @@ void tlsv1_server_set_log_cb(struct tlsv1_server *conn, } +int tlsv1_server_get_failed(struct tlsv1_server *conn) +{ + return conn->state == FAILED; +} + + +int tlsv1_server_get_read_alerts(struct tlsv1_server *conn) +{ + return conn->read_alerts; +} + + +int tlsv1_server_get_write_alerts(struct tlsv1_server *conn) +{ + return conn->write_alerts; +} + + #ifdef CONFIG_TESTING_OPTIONS void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags) { diff --git a/src/tls/tlsv1_server.h b/src/tls/tlsv1_server.h index 10e7699312b06..c9c0875ca3308 100644 --- a/src/tls/tlsv1_server.h +++ b/src/tls/tlsv1_server.h @@ -1,6 +1,6 @@ /* * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -19,6 +19,7 @@ struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred); void tlsv1_server_deinit(struct tlsv1_server *conn); int tlsv1_server_established(struct tlsv1_server *conn); int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len); u8 * tlsv1_server_handshake(struct tlsv1_server *conn, const u8 *in_data, size_t in_len, size_t *out_len); @@ -48,6 +49,10 @@ void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn, void tlsv1_server_set_log_cb(struct tlsv1_server *conn, void (*cb)(void *ctx, const char *msg), void *ctx); +int tlsv1_server_get_failed(struct tlsv1_server *conn); +int tlsv1_server_get_read_alerts(struct tlsv1_server *conn); +int tlsv1_server_get_write_alerts(struct tlsv1_server *conn); + void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags); #endif /* TLSV1_SERVER_H */ diff --git a/src/tls/tlsv1_server_i.h b/src/tls/tlsv1_server_i.h index 29c6678772150..2622585d84d0d 100644 --- a/src/tls/tlsv1_server_i.h +++ b/src/tls/tlsv1_server_i.h @@ -30,6 +30,8 @@ struct tlsv1_server { u8 alert_level; u8 alert_description; + int read_alerts, write_alerts; + struct crypto_public_key *client_rsa_key; struct tls_verify_hash verify; diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c index 4aa8a019f3e6e..e957678fc0d99 100644 --- a/src/tls/tlsv1_server_read.c +++ b/src/tls/tlsv1_server_read.c @@ -139,8 +139,11 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, pos = in_data; left = *in_len; - if (left < 4) + if (left < 4) { + tlsv1_server_log(conn, + "Truncated handshake message (expected ClientHello)"); goto decode_error; + } /* HandshakeType msg_type */ if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { @@ -157,8 +160,12 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, pos += 3; left -= 4; - if (len > left) + if (len > left) { + tlsv1_server_log(conn, + "Truncated ClientHello (len=%d left=%d)", + (int) len, (int) left); goto decode_error; + } /* body - ClientHello */ @@ -166,8 +173,10 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, end = pos + len; /* ProtocolVersion client_version */ - if (end - pos < 2) + if (end - pos < 2) { + tlsv1_server_log(conn, "Truncated ClientHello/client_version"); goto decode_error; + } conn->client_version = WPA_GET_BE16(pos); tlsv1_server_log(conn, "Client version %d.%d", conn->client_version >> 8, @@ -196,8 +205,10 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, tls_version_str(conn->rl.tls_version)); /* Random random */ - if (end - pos < TLS_RANDOM_LEN) + if (end - pos < TLS_RANDOM_LEN) { + tlsv1_server_log(conn, "Truncated ClientHello/client_random"); goto decode_error; + } os_memcpy(conn->client_random, pos, TLS_RANDOM_LEN); pos += TLS_RANDOM_LEN; @@ -205,25 +216,36 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, conn->client_random, TLS_RANDOM_LEN); /* SessionID session_id */ - if (end - pos < 1) + if (end - pos < 1) { + tlsv1_server_log(conn, "Truncated ClientHello/session_id len"); goto decode_error; - if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) + } + if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) { + tlsv1_server_log(conn, "Truncated ClientHello/session_id"); goto decode_error; + } wpa_hexdump(MSG_MSGDUMP, "TLSv1: client session_id", pos + 1, *pos); pos += 1 + *pos; /* TODO: add support for session resumption */ /* CipherSuite cipher_suites<2..2^16-1> */ - if (end - pos < 2) + if (end - pos < 2) { + tlsv1_server_log(conn, + "Truncated ClientHello/cipher_suites len"); goto decode_error; + } num_suites = WPA_GET_BE16(pos); pos += 2; - if (end - pos < num_suites) + if (end - pos < num_suites) { + tlsv1_server_log(conn, "Truncated ClientHello/cipher_suites"); goto decode_error; + } wpa_hexdump(MSG_MSGDUMP, "TLSv1: client cipher suites", pos, num_suites); - if (num_suites & 1) + if (num_suites & 1) { + tlsv1_server_log(conn, "Odd len ClientHello/cipher_suites"); goto decode_error; + } num_suites /= 2; cipher_suite = 0; @@ -259,11 +281,17 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, conn->cipher_suite = cipher_suite; /* CompressionMethod compression_methods<1..2^8-1> */ - if (end - pos < 1) + if (end - pos < 1) { + tlsv1_server_log(conn, + "Truncated ClientHello/compression_methods len"); goto decode_error; + } num_suites = *pos++; - if (end - pos < num_suites) + if (end - pos < num_suites) { + tlsv1_server_log(conn, + "Truncated ClientHello/compression_methods"); goto decode_error; + } wpa_hexdump(MSG_MSGDUMP, "TLSv1: client compression_methods", pos, num_suites); compr_null_found = 0; @@ -1217,6 +1245,7 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { tlsv1_server_log(conn, "Mismatch in verify_data"); + conn->state = FAILED; return -1; } diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c index bdc6c11992384..8d36cf1353910 100644 --- a/src/tls/tlsv1_server_write.c +++ b/src/tls/tlsv1_server_write.c @@ -26,7 +26,7 @@ static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn) size_t len = 0; struct x509_certificate *cert; - cert = conn->cred->cert; + cert = conn->cred ? conn->cred->cert : NULL; while (cert) { len += 3 + cert->cert_len; if (x509_certificate_self_signed(cert)) @@ -53,6 +53,9 @@ static int tls_write_server_hello(struct tlsv1_server *conn, pos += TLS_RECORD_HEADER_LEN; os_get_time(&now); +#ifdef TEST_FUZZ + now.sec = 0xfffefdfc; +#endif /* TEST_FUZZ */ WPA_PUT_BE32(conn->server_random, now.sec); if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c index f80c9a358bf52..fa4d44229622d 100644 --- a/src/tls/x509v3.c +++ b/src/tls/x509v3.c @@ -532,6 +532,8 @@ void x509_name_string(struct x509_name *name, char *buf, size_t len) } done: + if (pos < end) + *pos = '\0'; end[-1] = '\0'; } diff --git a/src/utils/Makefile b/src/utils/Makefile index 52efc5321fca1..1ee2bee67f6b3 100644 --- a/src/utils/Makefile +++ b/src/utils/Makefile @@ -19,6 +19,7 @@ LIB_OBJS= \ common.o \ crc32.o \ ip_addr.o \ + json.o \ radiotap.o \ trace.o \ uuid.o \ diff --git a/src/utils/base64.c b/src/utils/base64.c index 8eb4ba127d480..53a92f49ed83f 100644 --- a/src/utils/base64.c +++ b/src/utils/base64.c @@ -1,12 +1,13 @@ /* * Base64 encoding/decoding (RFC1341) - * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2019, 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 <stdint.h> #include "os.h" #include "base64.h" @@ -27,6 +28,8 @@ static unsigned char * base64_gen_encode(const unsigned char *src, size_t len, size_t olen; int line_len; + if (len >= SIZE_MAX / 4) + return NULL; olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */ if (add_pad) olen += olen / 72; /* line feeds */ diff --git a/src/utils/browser.c b/src/utils/browser.c index 9cf6152d6cbd8..ad0b382fbc119 100644 --- a/src/utils/browser.c +++ b/src/utils/browser.c @@ -166,8 +166,7 @@ int hs20_web_browser(const char *url) g_object_set(G_OBJECT(s), "ssl-strict", FALSE, NULL); ctx.win = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_wmclass(GTK_WINDOW(ctx.win), "Hotspot 2.0 client", - "Hotspot 2.0 client"); + gtk_window_set_role(GTK_WINDOW(ctx.win), "Hotspot 2.0 client"); gtk_window_set_default_size(GTK_WINDOW(ctx.win), 800, 600); scroll = gtk_scrolled_window_new(NULL, NULL); diff --git a/src/utils/common.c b/src/utils/common.c index 1eb33705bef34..b9c8bfdb98e95 100644 --- a/src/utils/common.c +++ b/src/utils/common.c @@ -1,6 +1,6 @@ /* * wpa_supplicant/hostapd / common helper functions, etc. - * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -1073,7 +1073,8 @@ size_t utf8_unescape(const char *inp, size_t in_size, in_size--; } - while (in_size--) { + while (in_size) { + in_size--; if (res_size >= out_size) return 0; @@ -1084,8 +1085,9 @@ size_t utf8_unescape(const char *inp, size_t in_size, return res_size; case '\\': - if (!in_size--) + if (!in_size) return 0; + in_size--; inp++; /* fall through */ @@ -1116,7 +1118,8 @@ size_t utf8_escape(const char *inp, size_t in_size, if (!in_size) in_size = os_strlen(inp); - while (in_size--) { + while (in_size) { + in_size--; if (res_size++ >= out_size) return 0; @@ -1221,3 +1224,28 @@ u8 rssi_to_rcpi(int rssi) return 220; return (rssi + 110) * 2; } + + +char * get_param(const char *cmd, const char *param) +{ + const char *pos, *end; + char *val; + size_t len; + + pos = os_strstr(cmd, param); + if (!pos) + return NULL; + + pos += os_strlen(param); + end = os_strchr(pos, ' '); + if (end) + len = end - pos; + else + len = os_strlen(pos); + val = os_malloc(len + 1); + if (!val) + return NULL; + os_memcpy(val, pos, len); + val[len] = '\0'; + return val; +} diff --git a/src/utils/common.h b/src/utils/common.h index f824d001aeab8..792a30ab9bbeb 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -567,6 +567,7 @@ int is_ctrl_char(char c); int str_starts(const char *str, const char *start); u8 rssi_to_rcpi(int rssi); +char * get_param(const char *cmd, const char *param); /* * gcc 4.4 ends up generating strict-aliasing warnings about some very common diff --git a/src/utils/const_time.h b/src/utils/const_time.h new file mode 100644 index 0000000000000..ab8f611ef6931 --- /dev/null +++ b/src/utils/const_time.h @@ -0,0 +1,191 @@ +/* + * Helper functions for constant time operations + * Copyright (c) 2019, The Linux Foundation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * These helper functions can be used to implement logic that needs to minimize + * externally visible differences in execution path by avoiding use of branches, + * avoiding early termination or other time differences, and forcing same memory + * access pattern regardless of values. + */ + +#ifndef CONST_TIME_H +#define CONST_TIME_H + + +#if defined(__clang__) +#define NO_UBSAN_UINT_OVERFLOW \ + __attribute__((no_sanitize("unsigned-integer-overflow"))) +#else +#define NO_UBSAN_UINT_OVERFLOW +#endif + + +/** + * const_time_fill_msb - Fill all bits with MSB value + * @val: Input value + * Returns: Value with all the bits set to the MSB of the input val + */ +static inline unsigned int const_time_fill_msb(unsigned int val) +{ + /* Move the MSB to LSB and multiple by -1 to fill in all bits. */ + return (val >> (sizeof(val) * 8 - 1)) * ~0U; +} + + +/* Returns: -1 if val is zero; 0 if val is not zero */ +static inline unsigned int const_time_is_zero(unsigned int val) + NO_UBSAN_UINT_OVERFLOW +{ + /* Set MSB to 1 for 0 and fill rest of bits with the MSB value */ + return const_time_fill_msb(~val & (val - 1)); +} + + +/* Returns: -1 if a == b; 0 if a != b */ +static inline unsigned int const_time_eq(unsigned int a, unsigned int b) +{ + return const_time_is_zero(a ^ b); +} + + +/* Returns: -1 if a == b; 0 if a != b */ +static inline u8 const_time_eq_u8(unsigned int a, unsigned int b) +{ + return (u8) const_time_eq(a, b); +} + + +/** + * const_time_eq_bin - Constant time memory comparison + * @a: First buffer to compare + * @b: Second buffer to compare + * @len: Number of octets to compare + * Returns: -1 if buffers are equal, 0 if not + * + * This function is meant for comparing passwords or hash values where + * difference in execution time or memory access pattern could provide external + * observer information about the location of the difference in the memory + * buffers. The return value does not behave like memcmp(), i.e., + * const_time_eq_bin() cannot be used to sort items into a defined order. Unlike + * memcmp(), the execution time of const_time_eq_bin() does not depend on the + * contents of the compared memory buffers, but only on the total compared + * length. + */ +static inline unsigned int const_time_eq_bin(const void *a, const void *b, + size_t len) +{ + const u8 *aa = a; + const u8 *bb = b; + size_t i; + u8 res = 0; + + for (i = 0; i < len; i++) + res |= aa[i] ^ bb[i]; + + return const_time_is_zero(res); +} + + +/** + * const_time_select - Constant time unsigned int selection + * @mask: 0 (false) or -1 (true) to identify which value to select + * @true_val: Value to select for the true case + * @false_val: Value to select for the false case + * Returns: true_val if mask == -1, false_val if mask == 0 + */ +static inline unsigned int const_time_select(unsigned int mask, + unsigned int true_val, + unsigned int false_val) +{ + return (mask & true_val) | (~mask & false_val); +} + + +/** + * const_time_select_int - Constant time int selection + * @mask: 0 (false) or -1 (true) to identify which value to select + * @true_val: Value to select for the true case + * @false_val: Value to select for the false case + * Returns: true_val if mask == -1, false_val if mask == 0 + */ +static inline int const_time_select_int(unsigned int mask, int true_val, + int false_val) +{ + return (int) const_time_select(mask, (unsigned int) true_val, + (unsigned int) false_val); +} + + +/** + * const_time_select_u8 - Constant time u8 selection + * @mask: 0 (false) or -1 (true) to identify which value to select + * @true_val: Value to select for the true case + * @false_val: Value to select for the false case + * Returns: true_val if mask == -1, false_val if mask == 0 + */ +static inline u8 const_time_select_u8(u8 mask, u8 true_val, u8 false_val) +{ + return (u8) const_time_select(mask, true_val, false_val); +} + + +/** + * const_time_select_s8 - Constant time s8 selection + * @mask: 0 (false) or -1 (true) to identify which value to select + * @true_val: Value to select for the true case + * @false_val: Value to select for the false case + * Returns: true_val if mask == -1, false_val if mask == 0 + */ +static inline s8 const_time_select_s8(u8 mask, s8 true_val, s8 false_val) +{ + return (s8) const_time_select(mask, (unsigned int) true_val, + (unsigned int) false_val); +} + + +/** + * const_time_select_bin - Constant time binary buffer selection copy + * @mask: 0 (false) or -1 (true) to identify which value to copy + * @true_val: Buffer to copy for the true case + * @false_val: Buffer to copy for the false case + * @len: Number of octets to copy + * @dst: Destination buffer for the copy + * + * This function copies the specified buffer into the destination buffer using + * operations with identical memory access pattern regardless of which buffer + * is being copied. + */ +static inline void const_time_select_bin(u8 mask, const u8 *true_val, + const u8 *false_val, size_t len, + u8 *dst) +{ + size_t i; + + for (i = 0; i < len; i++) + dst[i] = const_time_select_u8(mask, true_val[i], false_val[i]); +} + + +static inline int const_time_memcmp(const void *a, const void *b, size_t len) +{ + const u8 *aa = a; + const u8 *bb = b; + int diff, res = 0; + unsigned int mask; + + if (len == 0) + return 0; + do { + len--; + diff = (int) aa[len] - (int) bb[len]; + mask = const_time_is_zero((unsigned int) diff); + res = const_time_select_int(mask, res, diff); + } while (len); + + return res; +} + +#endif /* CONST_TIME_H */ diff --git a/src/utils/eloop.c b/src/utils/eloop.c index 436bc8c993389..bb375be1095e6 100644 --- a/src/utils/eloop.c +++ b/src/utils/eloop.c @@ -224,22 +224,25 @@ static int eloop_sock_queue(int sock, eloop_event_type type) #ifdef CONFIG_ELOOP_KQUEUE -static int eloop_sock_queue(int sock, eloop_event_type type) -{ - int filter; - struct kevent ke; +static short event_type_kevent_filter(eloop_event_type type) +{ switch (type) { case EVENT_TYPE_READ: - filter = EVFILT_READ; - break; + return EVFILT_READ; case EVENT_TYPE_WRITE: - filter = EVFILT_WRITE; - break; + return EVFILT_WRITE; default: - filter = 0; + return 0; } - EV_SET(&ke, sock, filter, EV_ADD, 0, 0, 0); +} + + +static int eloop_sock_queue(int sock, eloop_event_type type) +{ + struct kevent ke; + + EV_SET(&ke, sock, event_type_kevent_filter(type), EV_ADD, 0, 0, 0); if (kevent(eloop.kqueuefd, &ke, 1, NULL, 0, NULL) == -1) { wpa_printf(MSG_ERROR, "%s: kevent(ADD) for fd=%d failed: %s", __func__, sock, strerror(errno)); @@ -247,6 +250,7 @@ static int eloop_sock_queue(int sock, eloop_event_type type) } return 0; } + #endif /* CONFIG_ELOOP_KQUEUE */ @@ -301,7 +305,7 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, #endif /* CONFIG_ELOOP_POLL */ #if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE) if (new_max_sock >= eloop.max_fd) { - next = eloop.max_fd == 0 ? 16 : eloop.max_fd * 2; + next = new_max_sock + 16; temp_table = os_realloc_array(eloop.fd_table, next, sizeof(struct eloop_sock)); if (temp_table == NULL) @@ -411,7 +415,8 @@ static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, os_memset(&eloop.fd_table[sock], 0, sizeof(struct eloop_sock)); #endif /* CONFIG_ELOOP_EPOLL */ #ifdef CONFIG_ELOOP_KQUEUE - EV_SET(&ke, sock, 0, EV_DELETE, 0, 0, 0); + EV_SET(&ke, sock, event_type_kevent_filter(table->type), EV_DELETE, 0, + 0, 0); if (kevent(eloop.kqueuefd, &ke, 1, NULL, 0, NULL) < 0) { wpa_printf(MSG_ERROR, "%s: kevent(DEL) for fd=%d failed: %s", __func__, sock, strerror(errno)); diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c index 58519ea8d2489..e62fbf96bcb32 100644 --- a/src/utils/http_curl.c +++ b/src/utils/http_curl.c @@ -31,6 +31,14 @@ #endif /* EAP_TLS_OPENSSL */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L +static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x) +{ + return ASN1_STRING_data((ASN1_STRING *) x); +} +#endif /* OpenSSL < 1.1.0 */ + + struct http_ctx { void *ctx; struct xml_node_ctx *xml; @@ -446,6 +454,7 @@ sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_IA5STRING) *, (st))) #define sk_ASN1_IA5STRING_value(st, i) (ASN1_IA5STRING *) \ sk_value(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_IA5STRING) *, (st)), (i)) #else /* OPENSSL_IS_BORINGSSL */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L #define sk_LogotypeInfo_num(st) SKM_sk_num(LogotypeInfo, (st)) #define sk_LogotypeInfo_value(st, i) SKM_sk_value(LogotypeInfo, (st), (i)) #define sk_LogotypeImage_num(st) SKM_sk_num(LogotypeImage, (st)) @@ -456,6 +465,13 @@ sk_value(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_IA5STRING) *, (st)), (i)) #define sk_HashAlgAndValue_value(st, i) SKM_sk_value(HashAlgAndValue, (st), (i)) #define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st)) #define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i)) +#else +DEFINE_STACK_OF(LogotypeInfo) +DEFINE_STACK_OF(LogotypeImage) +DEFINE_STACK_OF(LogotypeAudio) +DEFINE_STACK_OF(HashAlgAndValue) +DEFINE_STACK_OF(ASN1_IA5STRING) +#endif #endif /* OPENSSL_IS_BORINGSSL */ @@ -486,7 +502,8 @@ static void add_logo(struct http_ctx *ctx, struct http_cert *hcert, return; n->hash_len = ASN1_STRING_length(hash->hashValue); - n->hash = os_memdup(ASN1_STRING_data(hash->hashValue), n->hash_len); + n->hash = os_memdup(ASN1_STRING_get0_data(hash->hashValue), + n->hash_len); if (n->hash == NULL) { os_free(n->alg_oid); return; @@ -499,7 +516,7 @@ static void add_logo(struct http_ctx *ctx, struct http_cert *hcert, os_free(n->hash); return; } - os_memcpy(n->uri, ASN1_STRING_data(uri), len); + os_memcpy(n->uri, ASN1_STRING_get0_data(uri), len); n->uri[len] = '\0'; hcert->num_logo++; @@ -814,9 +831,9 @@ static void add_logotype_ext(struct http_ctx *ctx, struct http_cert *hcert, } wpa_hexdump(MSG_DEBUG, "logotypeExtn", - ASN1_STRING_data(os), ASN1_STRING_length(os)); + ASN1_STRING_get0_data(os), ASN1_STRING_length(os)); - data = ASN1_STRING_data(os); + data = ASN1_STRING_get0_data(os); logo = d2i_LogotypeExtn(NULL, &data, ASN1_STRING_length(os)); if (logo == NULL) { wpa_printf(MSG_INFO, "Failed to parse logotypeExtn"); @@ -1136,7 +1153,7 @@ static int ocsp_resp_cb(SSL *s, void *arg) return 0; } - store = SSL_CTX_get_cert_store(s->ctx); + store = SSL_CTX_get_cert_store(SSL_get_SSL_CTX(s)); if (ctx->peer_issuer) { wpa_printf(MSG_DEBUG, "OpenSSL: Add issuer"); debug_dump_cert("OpenSSL: Issuer certificate", @@ -1272,12 +1289,13 @@ static int ocsp_resp_cb(SSL *s, void *arg) } +#if OPENSSL_VERSION_NUMBER < 0x10100000L static SSL_METHOD patch_ssl_method; static const SSL_METHOD *real_ssl_method; static int curl_patch_ssl_new(SSL *s) { - SSL_CTX *ssl = s->ctx; + SSL_CTX *ssl = SSL_get_SSL_CTX(s); int ret; ssl->method = real_ssl_method; @@ -1288,6 +1306,7 @@ static int curl_patch_ssl_new(SSL *s) return ret; } +#endif /* OpenSSL < 1.1.0 */ #endif /* HAVE_OCSP */ @@ -1306,6 +1325,7 @@ static CURLcode curl_cb_ssl(CURL *curl, void *sslctx, void *parm) SSL_CTX_set_tlsext_status_cb(ssl, ocsp_resp_cb); SSL_CTX_set_tlsext_status_arg(ssl, ctx); +#if OPENSSL_VERSION_NUMBER < 0x10100000L /* * Use a temporary SSL_METHOD to get a callback on SSL_new() * from libcurl since there is no proper callback registration @@ -1315,6 +1335,7 @@ static CURLcode curl_cb_ssl(CURL *curl, void *sslctx, void *parm) patch_ssl_method.ssl_new = curl_patch_ssl_new; real_ssl_method = ssl->method; ssl->method = &patch_ssl_method; +#endif /* OpenSSL < 1.1.0 */ } #endif /* HAVE_OCSP */ @@ -1351,7 +1372,7 @@ static CURL * setup_curl_post(struct http_ctx *ctx, const char *address, #ifdef EAP_TLS_OPENSSL curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, curl_cb_ssl); curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, ctx); -#ifdef OPENSSL_IS_BORINGSSL +#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* For now, using the CURLOPT_SSL_VERIFYSTATUS option only * with BoringSSL since the OpenSSL specific callback hack to * enable OCSP is not available with BoringSSL. The OCSP diff --git a/src/utils/json.c b/src/utils/json.c index b9130d3a65ab0..b64433959ff7b 100644 --- a/src/utils/json.c +++ b/src/utils/json.c @@ -103,6 +103,11 @@ static char * json_parse_string(const char **json_pos, const char *end) return str; case '\\': pos++; + if (pos >= end) { + wpa_printf(MSG_DEBUG, + "JSON: Truncated \\ escape"); + goto fail; + } switch (*pos) { case '"': case '\\': @@ -165,6 +170,8 @@ static int json_parse_number(const char **json_pos, const char *end, break; } } + if (pos == end) + pos--; if (pos < *json_pos) return -1; len = pos - *json_pos + 1; diff --git a/src/utils/list.h b/src/utils/list.h index ee2f4856950f1..85aa5e39cfe16 100644 --- a/src/utils/list.h +++ b/src/utils/list.h @@ -1,6 +1,6 @@ /* * Doubly-linked list - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -76,8 +76,8 @@ static inline unsigned int dl_list_len(struct dl_list *list) dl_list_entry((list)->prev, type, member)) #define dl_list_for_each(item, list, type, member) \ - for (item = dl_list_entry((list)->next, type, member); \ - &item->member != (list); \ + for (item = dl_list_first((list), type, member); \ + item && item != dl_list_entry((list), type, member); \ item = dl_list_entry(item->member.next, type, member)) #define dl_list_for_each_safe(item, n, list, type, member) \ diff --git a/src/utils/os_internal.c b/src/utils/os_internal.c index ed6eb3c6b6770..474c8a372205f 100644 --- a/src/utils/os_internal.c +++ b/src/utils/os_internal.c @@ -430,22 +430,6 @@ int os_strncmp(const char *s1, const char *s2, size_t n) } -char * os_strncpy(char *dest, const char *src, size_t n) -{ - char *d = dest; - - while (n--) { - *d = *src; - if (*src == '\0') - break; - d++; - src++; - } - - return dest; -} - - size_t os_strlcpy(char *dest, const char *src, size_t siz) { const char *s = src; diff --git a/src/utils/os_none.c b/src/utils/os_none.c index e74f206a2c5a2..5e0a3ada66788 100644 --- a/src/utils/os_none.c +++ b/src/utils/os_none.c @@ -218,12 +218,6 @@ int os_strncmp(const char *s1, const char *s2, size_t n) } -char * os_strncpy(char *dest, const char *src, size_t n) -{ - return dest; -} - - size_t os_strlcpy(char *dest, const char *src, size_t size) { return 0; diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c index 1894fcdb0cf25..800c50772d893 100644 --- a/src/utils/os_unix.c +++ b/src/utils/os_unix.c @@ -1,6 +1,6 @@ /* * OS specific functions for UNIX/POSIX systems - * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -250,6 +250,13 @@ void os_daemonize_terminate(const char *pid_file) int os_get_random(unsigned char *buf, size_t len) { +#ifdef TEST_FUZZ + size_t i; + + for (i = 0; i < len; i++) + buf[i] = i & 0xff; + return 0; +#else /* TEST_FUZZ */ FILE *f; size_t rc; @@ -266,6 +273,7 @@ int os_get_random(unsigned char *buf, size_t len) fclose(f); return rc != len ? -1 : 0; +#endif /* TEST_FUZZ */ } @@ -512,7 +520,7 @@ void * os_memdup(const void *src, size_t len) { void *r = os_malloc(len); - if (r) + if (r && src) os_memcpy(r, src, len); return r; } diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c index 1b8ff82b41731..3af4fcde1daa8 100644 --- a/src/utils/utils_module_tests.c +++ b/src/utils/utils_module_tests.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/const_time.h" #include "common/ieee802_11_defs.h" #include "utils/bitfield.h" #include "utils/ext_password.h" @@ -919,6 +920,294 @@ static int json_tests(void) } +static int const_time_tests(void) +{ + struct const_time_fill_msb_test { + unsigned int val; + unsigned int expected; + } const_time_fill_msb_tests[] = { + { 0, 0 }, + { 1, 0 }, + { 2, 0 }, + { 1 << (sizeof(unsigned int) * 8 - 1), ~0 }, + { ~0 - 1, ~0 }, + { ~0, ~0 } + }; + struct const_time_is_zero_test { + unsigned int val; + unsigned int expected; + } const_time_is_zero_tests[] = { + { 0, ~0 }, + { 1, 0 }, + { 2, 0 }, + { 1 << (sizeof(unsigned int) * 8 - 1), 0 }, + { ~0 - 1, 0 }, + { ~0, 0 } + }; + struct const_time_eq_test { + unsigned int a; + unsigned int b; + unsigned int expected; + unsigned int expected_u8; + } const_time_eq_tests[] = { + { 0, 1, 0, 0 }, + { 1, 2, 0, 0 }, + { 1, 1, ~0, 0xff }, + { ~0, ~0, ~0, 0xff }, + { ~0, ~0 - 1, 0, 0 }, + { 0, 0, ~0, 0xff } + }; + struct const_time_eq_bin_test { + u8 *a; + u8 *b; + size_t len; + unsigned int expected; + } const_time_eq_bin_tests[] = { + { (u8 *) "", (u8 *) "", 0, ~0 }, + { (u8 *) "abcde", (u8 *) "abcde", 5, ~0 }, + { (u8 *) "abcde", (u8 *) "Abcde", 5, 0 }, + { (u8 *) "abcde", (u8 *) "aBcde", 5, 0 }, + { (u8 *) "abcde", (u8 *) "abCde", 5, 0 }, + { (u8 *) "abcde", (u8 *) "abcDe", 5, 0 }, + { (u8 *) "abcde", (u8 *) "abcdE", 5, 0 }, + { (u8 *) "\x00", (u8 *) "\x01", 1, 0 }, + { (u8 *) "\x00", (u8 *) "\x80", 1, 0 }, + { (u8 *) "\x00", (u8 *) "\x00", 1, ~0 } + }; + struct const_time_select_test { + unsigned int mask; + unsigned int true_val; + unsigned int false_val; + unsigned int expected; + } const_time_select_tests[] = { + { ~0, ~0, ~0, ~0 }, + { 0, ~0, ~0, ~0 }, + { ~0, ~0, 0, ~0 }, + { 0, ~0, 0, 0 }, + { ~0, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa }, + { 0, 0xaaaaaaaa, 0x55555555, 0x55555555 }, + { ~0, 3, 3, 3 }, + { 0, 3, 3, 3 }, + { ~0, 1, 2, 1 }, + { 0, 1, 2, 2 } + }; + struct const_time_select_int_test { + unsigned int mask; + int true_val; + int false_val; + int expected; + } const_time_select_int_tests[] = { + { ~0, -128, 127, -128 }, + { 0, -128, 127, 127 }, + { ~0, -2147483648, 2147483647, -2147483648 }, + { 0, -2147483648, 2147483647, 2147483647 }, + { ~0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { ~0, -1, 1, -1 }, + { 0, -1, 1, 1 } + }; + struct const_time_select_u8_test { + u8 mask; + u8 true_val; + u8 false_val; + u8 expected; + } const_time_select_u8_tests[] = { + { ~0, ~0, ~0, ~0 }, + { 0, ~0, ~0, ~0 }, + { ~0, ~0, 0, ~0 }, + { 0, ~0, 0, 0 }, + { ~0, 0xaa, 0x55, 0xaa }, + { 0, 0xaa, 0x55, 0x55 }, + { ~0, 1, 2, 1 }, + { 0, 1, 2, 2 } + }; + struct const_time_select_s8_test { + u8 mask; + s8 true_val; + s8 false_val; + s8 expected; + } const_time_select_s8_tests[] = { + { ~0, -128, 127, -128 }, + { 0, -128, 127, 127 }, + { ~0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { ~0, -1, 1, -1 }, + { 0, -1, 1, 1 } + }; + struct const_time_select_bin_test { + u8 mask; + u8 *true_val; + u8 *false_val; + size_t len; + u8 *expected; + } const_time_select_bin_tests[] = { + { ~0, (u8 *) "abcde", (u8 *) "ABCDE", 5, (u8 *) "abcde" }, + { 0, (u8 *) "abcde", (u8 *) "ABCDE", 5, (u8 *) "ABCDE" }, + { ~0, (u8 *) "", (u8 *) "", 0, (u8 *) "" }, + { 0, (u8 *) "", (u8 *) "", 0, (u8 *) "" } + }; + struct const_time_memcmp_test { + char *a; + char *b; + size_t len; + int expected; + } const_time_memcmp_tests[] = { + { "abcde", "abcde", 5, 0 }, + { "abcde", "bbcde", 5, -1 }, + { "bbcde", "abcde", 5, 1 }, + { "accde", "abcde", 5, 1 }, + { "abcee", "abcde", 5, 1 }, + { "abcdf", "abcde", 5, 1 }, + { "cbcde", "aXXXX", 5, 2 }, + { "a", "d", 1, -3 }, + { "", "", 0, 0 } + }; + unsigned int i; + int ret = 0; + + wpa_printf(MSG_INFO, "constant time tests"); + + for (i = 0; i < ARRAY_SIZE(const_time_fill_msb_tests); i++) { + struct const_time_fill_msb_test *test; + + test = &const_time_fill_msb_tests[i]; + if (const_time_fill_msb(test->val) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_fill_msb(0x%x) test failed", + test->val); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_is_zero_tests); i++) { + struct const_time_is_zero_test *test; + + test = &const_time_is_zero_tests[i]; + if (const_time_is_zero(test->val) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_is_zero(0x%x) test failed", + test->val); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_eq_tests); i++) { + struct const_time_eq_test *test; + + test = &const_time_eq_tests[i]; + if (const_time_eq(test->a, test->b) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_eq(0x%x,0x%x) test failed", + test->a, test->b); + ret = -1; + } + if (const_time_eq_u8(test->a, test->b) != test->expected_u8) { + wpa_printf(MSG_ERROR, + "const_time_eq_u8(0x%x,0x%x) test failed", + test->a, test->b); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_eq_bin_tests); i++) { + struct const_time_eq_bin_test *test; + + test = &const_time_eq_bin_tests[i]; + if (const_time_eq_bin(test->a, test->b, test->len) != + test->expected) { + wpa_printf(MSG_ERROR, + "const_time_eq_bin(len=%u) test failed", + (unsigned int) test->len); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_select_tests); i++) { + struct const_time_select_test *test; + + test = &const_time_select_tests[i]; + if (const_time_select(test->mask, test->true_val, + test->false_val) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_select(0x%x,0x%x,0x%x) test failed", + test->mask, test->true_val, test->false_val); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_select_int_tests); i++) { + struct const_time_select_int_test *test; + + test = &const_time_select_int_tests[i]; + if (const_time_select_int(test->mask, test->true_val, + test->false_val) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_select_int(0x%x,%d,%d) test failed", + test->mask, test->true_val, test->false_val); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_select_u8_tests); i++) { + struct const_time_select_u8_test *test; + + test = &const_time_select_u8_tests[i]; + if (const_time_select_u8(test->mask, test->true_val, + test->false_val) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_select_u8(0x%x,0x%x,0x%x) test failed", + test->mask, test->true_val, test->false_val); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_select_s8_tests); i++) { + struct const_time_select_s8_test *test; + + test = &const_time_select_s8_tests[i]; + if (const_time_select_s8(test->mask, test->true_val, + test->false_val) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_select_s8(0x%x,0x%x,0x%x) test failed", + test->mask, test->true_val, test->false_val); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_select_bin_tests); i++) { + struct const_time_select_bin_test *test; + u8 dst[100]; + + test = &const_time_select_bin_tests[i]; + const_time_select_bin(test->mask, test->true_val, + test->false_val, test->len, dst); + if (os_memcmp(dst, test->expected, test->len) != 0) { + wpa_printf(MSG_ERROR, + "const_time_select_bin(0x%x,%u) test failed", + test->mask, (unsigned int) test->len); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_memcmp_tests); i++) { + struct const_time_memcmp_test *test; + int res; + + test = &const_time_memcmp_tests[i]; + res = const_time_memcmp(test->a, test->b, test->len); + if (res != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_memcmp(%s,%s,%d) test failed (%d != %d)", + test->a, test->b, (int) test->len, + res, test->expected); + ret = -1; + } + } + + return ret; +} + + int utils_module_tests(void) { int ret = 0; @@ -936,6 +1225,7 @@ int utils_module_tests(void) ip_addr_tests() < 0 || eloop_tests() < 0 || json_tests() < 0 || + const_time_tests() < 0 || int_array_tests() < 0) ret = -1; diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c index a56462b8bbdc7..c437000a7f500 100644 --- a/src/utils/wpa_debug.c +++ b/src/utils/wpa_debug.c @@ -422,6 +422,12 @@ static void _wpa_hexdump_ascii(int level, const char *title, const void *buf, #ifdef CONFIG_ANDROID_LOG _wpa_hexdump(level, title, buf, len, show); #else /* CONFIG_ANDROID_LOG */ +#ifdef CONFIG_DEBUG_SYSLOG + if (wpa_debug_syslog) { + _wpa_hexdump(level, title, buf, len, show); + return; + } +#endif /* CONFIG_DEBUG_SYSLOG */ wpa_debug_print_timestamp(); #ifdef CONFIG_DEBUG_FILE if (out_file) { diff --git a/src/wps/wps.c b/src/wps/wps.c index 8d228270ff10c..484df262c3ca2 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -145,6 +145,8 @@ struct wps_data * wps_init(const struct wps_config *cfg) data->peer_pubkey_hash_set = 1; } + data->multi_ap_backhaul_sta = cfg->multi_ap_backhaul_sta; + return data; } @@ -430,7 +432,7 @@ struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type) if (wps_build_version(ie) || wps_build_req_type(ie, req_type) || - wps_build_wfa_ext(ie, 0, NULL, 0)) { + wps_build_wfa_ext(ie, 0, NULL, 0, 0)) { wpabuf_free(ie); return NULL; } @@ -464,7 +466,7 @@ struct wpabuf * wps_build_assoc_resp_ie(void) if (wps_build_version(ie) || wps_build_resp_type(ie, WPS_RESP_AP) || - wps_build_wfa_ext(ie, 0, NULL, 0)) { + wps_build_wfa_ext(ie, 0, NULL, 0, 0)) { wpabuf_free(ie); return NULL; } @@ -516,7 +518,7 @@ struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev, wps_build_model_name(dev, ie) || wps_build_model_number(dev, ie) || wps_build_dev_name(dev, ie) || - wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) || + wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0, 0) || wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types) || wps_build_secondary_dev_type(dev, ie) diff --git a/src/wps/wps.h b/src/wps/wps.h index 2505d2d9f246d..14ce863269f6c 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -100,6 +100,7 @@ struct wps_device_data { struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; int p2p; + u8 multi_ap_ext; }; /** @@ -187,6 +188,12 @@ struct wps_config { * peer_pubkey_hash - Peer public key hash or %NULL if not known */ const u8 *peer_pubkey_hash; + + /** + * multi_ap_backhaul_sta - Whether this is a Multi-AP backhaul STA + * enrollee + */ + int multi_ap_backhaul_sta; }; struct wps_data * wps_init(const struct wps_config *cfg); @@ -395,6 +402,37 @@ struct wps_registrar_config { * PSK is set for a network. */ int force_per_enrollee_psk; + + /** + * multi_ap_backhaul_ssid - SSID to supply to a Multi-AP backhaul + * enrollee + * + * This SSID is used by the Registrar to fill in information for + * Credentials when the enrollee advertises it is a Multi-AP backhaul + * STA. + */ + const u8 *multi_ap_backhaul_ssid; + + /** + * multi_ap_backhaul_ssid_len - Length of multi_ap_backhaul_ssid in + * octets + */ + size_t multi_ap_backhaul_ssid_len; + + /** + * multi_ap_backhaul_network_key - The Network Key (PSK) for the + * Multi-AP backhaul enrollee. + * + * This key can be either the ASCII passphrase (8..63 characters) or the + * 32-octet PSK (64 hex characters). + */ + const u8 *multi_ap_backhaul_network_key; + + /** + * multi_ap_backhaul_network_key_len - Length of + * multi_ap_backhaul_network_key in octets + */ + size_t multi_ap_backhaul_network_key_len; }; diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c index 770f5e90cbde0..4e872f37295c1 100644 --- a/src/wps/wps_attr_build.c +++ b/src/wps/wps_attr_build.c @@ -60,7 +60,8 @@ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg) } wps->dh_privkey = wpabuf_dup(wps->wps->ap_nfc_dh_privkey); pubkey = wpabuf_dup(wps->wps->ap_nfc_dh_pubkey); - wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, pubkey); + if (wps->dh_privkey && pubkey) + wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, pubkey); #endif /* CONFIG_WPS_NFC */ } else { wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys"); @@ -203,7 +204,8 @@ int wps_build_version(struct wpabuf *msg) int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll, - const u8 *auth_macs, size_t auth_macs_count) + const u8 *auth_macs, size_t auth_macs_count, + u8 multi_ap_subelem) { u8 *len; @@ -244,6 +246,14 @@ int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll, MAC2STR(&auth_macs[i * ETH_ALEN])); } + if (multi_ap_subelem) { + wpa_printf(MSG_DEBUG, "WPS: * Multi-AP (0x%x)", + multi_ap_subelem); + wpabuf_put_u8(msg, WFA_ELEM_MULTI_AP); + wpabuf_put_u8(msg, 1); /* length */ + wpabuf_put_u8(msg, multi_ap_subelem); + } + WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2); #ifdef CONFIG_WPS_TESTING diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c index 756d57e876c55..fd51635158ac5 100644 --- a/src/wps/wps_attr_parse.c +++ b/src/wps/wps_attr_parse.c @@ -67,6 +67,17 @@ static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr, } attr->registrar_configuration_methods = pos; break; + case WFA_ELEM_MULTI_AP: + if (len != 1) { + wpa_printf(MSG_DEBUG, + "WPS: Invalid Multi-AP Extension length %u", + len); + return -1; + } + attr->multi_ap_ext = *pos; + wpa_printf(MSG_DEBUG, "WPS: Multi-AP Extension 0x%02x", + attr->multi_ap_ext); + break; default: wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor " "Extension subelement %u", id); diff --git a/src/wps/wps_attr_parse.h b/src/wps/wps_attr_parse.h index 8188fe9173d44..4de27b26d4e26 100644 --- a/src/wps/wps_attr_parse.h +++ b/src/wps/wps_attr_parse.h @@ -97,6 +97,7 @@ struct wps_parse_attr { 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]; + u8 multi_ap_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 bcae1ba5887b6..747dc4710b20b 100644 --- a/src/wps/wps_common.c +++ b/src/wps/wps_common.c @@ -374,7 +374,7 @@ struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band, (rf_band && wps_build_rf_bands_attr(plain, rf_band)) || (channel && wps_build_ap_channel(plain, channel)) || wps_build_mac_addr(plain, wps->dev.mac_addr) || - wps_build_wfa_ext(plain, 0, NULL, 0)) { + wps_build_wfa_ext(plain, 0, NULL, 0, 0)) { os_free(data.new_psk); wpabuf_clear_free(plain); return NULL; @@ -421,7 +421,7 @@ struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id, if (wps_build_oob_dev_pw(data, dev_pw_id, pubkey, wpabuf_head(dev_pw), wpabuf_len(dev_pw)) || - wps_build_wfa_ext(data, 0, NULL, 0)) { + wps_build_wfa_ext(data, 0, NULL, 0, 0)) { wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password " "token"); wpabuf_clear_free(data); @@ -586,7 +586,7 @@ struct wpabuf * wps_build_wsc_ack(struct wps_data *wps) wps_build_msg_type(msg, WPS_WSC_ACK) || wps_build_enrollee_nonce(wps, msg) || wps_build_registrar_nonce(wps, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -610,7 +610,7 @@ struct wpabuf * wps_build_wsc_nack(struct wps_data *wps) wps_build_enrollee_nonce(wps, msg) || wps_build_registrar_nonce(wps, msg) || wps_build_config_error(msg, wps->config_error) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -726,7 +726,7 @@ struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx, if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER, nfc_dh_pubkey, NULL, 0) || wps_build_uuid_e(msg, ctx->uuid) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -809,7 +809,7 @@ struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx, wps_build_ssid(msg, ctx) || wps_build_ap_freq(msg, freq) || (bssid && wps_build_mac_addr(msg, bssid)) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -848,7 +848,7 @@ struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx, wps_build_rf_bands(&ctx->dev, msg, 0) || wps_build_serial_number(&ctx->dev, msg) || wps_build_uuid_e(msg, ctx->uuid) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -900,7 +900,7 @@ struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx, wps_build_rf_bands(&ctx->dev, msg, 0) || wps_build_serial_number(&ctx->dev, msg) || wps_build_uuid_e(msg, ctx->uuid) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h index 301864da433d0..9fccb4eeb5c10 100644 --- a/src/wps/wps_defs.h +++ b/src/wps/wps_defs.h @@ -152,7 +152,8 @@ enum { WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02, WFA_ELEM_REQUEST_TO_ENROLL = 0x03, WFA_ELEM_SETTINGS_DELAY_TIME = 0x04, - WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05 + WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05, + WFA_ELEM_MULTI_AP = 0x06 }; /* Device Password ID */ diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c index 0d01211a261cb..b209fea8a4f20 100644 --- a/src/wps/wps_dev_attr.c +++ b/src/wps/wps_dev_attr.c @@ -390,6 +390,14 @@ int wps_process_os_version(struct wps_device_data *dev, const u8 *ver) } +void wps_process_vendor_ext_m1(struct wps_device_data *dev, const u8 ext) +{ + dev->multi_ap_ext = ext; + wpa_printf(MSG_DEBUG, "WPS: Multi-AP extension value %02x", + dev->multi_ap_ext); +} + + int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands) { if (bands == NULL) { diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h index c9034addbcc64..a4b4173cdbaf4 100644 --- a/src/wps/wps_dev_attr.h +++ b/src/wps/wps_dev_attr.h @@ -29,6 +29,7 @@ int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg); int wps_process_device_attrs(struct wps_device_data *dev, struct wps_parse_attr *attr); int wps_process_os_version(struct wps_device_data *dev, const u8 *ver); +void wps_process_vendor_ext_m1(struct wps_device_data *dev, const u8 ext); int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands); void wps_device_data_free(struct wps_device_data *dev); int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg); diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c index 417507740d7aa..80ed603fc3847 100644 --- a/src/wps/wps_enrollee.c +++ b/src/wps/wps_enrollee.c @@ -105,6 +105,7 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) { struct wpabuf *msg; u16 config_methods; + u8 multi_ap_backhaul_sta = 0; if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0) return NULL; @@ -134,6 +135,9 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) WPS_CONFIG_PHY_PUSHBUTTON); } + if (wps->multi_ap_backhaul_sta) + multi_ap_backhaul_sta = MULTI_AP_BACKHAUL_STA; + if (wps_build_version(msg) || wps_build_msg_type(msg, WPS_M1) || wps_build_uuid_e(msg, wps->uuid_e) || @@ -152,7 +156,7 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) wps_build_dev_password_id(msg, wps->dev_pw_id) || wps_build_config_error(msg, WPS_CFG_NO_ERROR) || wps_build_os_version(&wps->wps->dev, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, multi_ap_backhaul_sta) || wps_build_vendor_ext_m1(&wps->wps->dev, msg)) { wpabuf_free(msg); return NULL; @@ -190,7 +194,7 @@ static struct wpabuf * wps_build_m3(struct wps_data *wps) wps_build_msg_type(msg, WPS_M3) || wps_build_registrar_nonce(wps, msg) || wps_build_e_hash(wps, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(msg); return NULL; @@ -223,7 +227,7 @@ static struct wpabuf * wps_build_m5(struct wps_data *wps) wps_build_e_snonce1(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_clear_free(plain); wpabuf_free(msg); @@ -393,7 +397,7 @@ static struct wpabuf * wps_build_m7(struct wps_data *wps) (wps->wps->ap && wps_build_ap_settings(wps, plain)) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_clear_free(plain); wpabuf_free(msg); @@ -430,7 +434,7 @@ static struct wpabuf * wps_build_wsc_done(struct wps_data *wps) wps_build_msg_type(msg, WPS_WSC_DONE) || wps_build_enrollee_nonce(wps, msg) || wps_build_registrar_nonce(wps, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c index affd6a4af38a3..06a8fdaf3459c 100644 --- a/src/wps/wps_er.c +++ b/src/wps/wps_er.c @@ -1530,7 +1530,7 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, wps_er_build_selected_registrar(msg, sel_reg) || wps_er_build_dev_password_id(msg, dev_passwd_id) || wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) || - wps_build_wfa_ext(msg, 0, auth_macs, count) || + wps_build_wfa_ext(msg, 0, auth_macs, count, 0) || wps_er_build_uuid_r(msg, er->wps->uuid)) { wpabuf_free(msg); return; @@ -2048,7 +2048,7 @@ struct wpabuf * wps_er_config_token_from_cred(struct wps_context *wps, data.wps = wps; data.use_cred = cred; if (wps_build_cred(&data, ret) || - wps_build_wfa_ext(ret, 0, NULL, 0)) { + wps_build_wfa_ext(ret, 0, NULL, 0, 0)) { wpabuf_free(ret); return NULL; } diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h index fe0c60bd120bf..2cf22d4b7a636 100644 --- a/src/wps/wps_i.h +++ b/src/wps/wps_i.h @@ -125,6 +125,8 @@ struct wps_data { int pbc_in_m1; struct wps_nfc_pw_token *nfc_pw_token; + + int multi_ap_backhaul_sta; }; @@ -163,7 +165,8 @@ int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg, struct wpabuf *plain); int wps_build_version(struct wpabuf *msg); int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll, - const u8 *auth_macs, size_t auth_macs_count); + const u8 *auth_macs, size_t auth_macs_count, + u8 multi_ap_subelem); int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type); int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg); int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg); diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index 379925e3f0a9e..0ac5b28313793 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -188,6 +188,37 @@ struct wps_registrar { #ifdef WPS_WORKAROUNDS struct os_reltime pbc_ignore_start; #endif /* WPS_WORKAROUNDS */ + + /** + * multi_ap_backhaul_ssid - SSID to supply to a Multi-AP backhaul + * enrollee + * + * This SSID is used by the Registrar to fill in information for + * Credentials when the enrollee advertises it is a Multi-AP backhaul + * STA. + */ + u8 multi_ap_backhaul_ssid[SSID_MAX_LEN]; + + /** + * multi_ap_backhaul_ssid_len - Length of multi_ap_backhaul_ssid in + * octets + */ + size_t multi_ap_backhaul_ssid_len; + + /** + * multi_ap_backhaul_network_key - The Network Key (PSK) for the + * Multi-AP backhaul enrollee. + * + * This key can be either the ASCII passphrase (8..63 characters) or the + * 32-octet PSK (64 hex characters). + */ + u8 *multi_ap_backhaul_network_key; + + /** + * multi_ap_backhaul_network_key_len - Length of + * multi_ap_backhaul_network_key in octets + */ + size_t multi_ap_backhaul_network_key_len; }; @@ -667,6 +698,22 @@ wps_registrar_init(struct wps_context *wps, reg->dualband = cfg->dualband; reg->force_per_enrollee_psk = cfg->force_per_enrollee_psk; + if (cfg->multi_ap_backhaul_ssid) { + os_memcpy(reg->multi_ap_backhaul_ssid, + cfg->multi_ap_backhaul_ssid, + cfg->multi_ap_backhaul_ssid_len); + reg->multi_ap_backhaul_ssid_len = + cfg->multi_ap_backhaul_ssid_len; + } + if (cfg->multi_ap_backhaul_network_key) { + reg->multi_ap_backhaul_network_key = + os_memdup(cfg->multi_ap_backhaul_network_key, + cfg->multi_ap_backhaul_network_key_len); + if (reg->multi_ap_backhaul_network_key) + reg->multi_ap_backhaul_network_key_len = + cfg->multi_ap_backhaul_network_key_len; + } + if (wps_set_ie(reg)) { wps_registrar_deinit(reg); return NULL; @@ -704,6 +751,8 @@ void wps_registrar_deinit(struct wps_registrar *reg) eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); wps_registrar_flush(reg); wpabuf_clear_free(reg->extra_cred); + bin_clear_free(reg->multi_ap_backhaul_network_key, + reg->multi_ap_backhaul_network_key_len); os_free(reg); } @@ -1281,7 +1330,7 @@ static int wps_set_ie(struct wps_registrar *reg) wps_build_sel_reg_config_methods(reg, beacon) || wps_build_sel_pbc_reg_uuid_e(reg, beacon) || (reg->dualband && wps_build_rf_bands(®->wps->dev, beacon, 0)) || - wps_build_wfa_ext(beacon, 0, auth_macs, count) || + wps_build_wfa_ext(beacon, 0, auth_macs, count, 0) || wps_build_vendor_ext(®->wps->dev, beacon)) { wpabuf_free(beacon); wpabuf_free(probe); @@ -1311,7 +1360,7 @@ static int wps_set_ie(struct wps_registrar *reg) wps_build_device_attrs(®->wps->dev, probe) || wps_build_probe_config_methods(reg, probe) || (reg->dualband && wps_build_rf_bands(®->wps->dev, probe, 0)) || - wps_build_wfa_ext(probe, 0, auth_macs, count) || + wps_build_wfa_ext(probe, 0, auth_macs, count, 0) || wps_build_vendor_ext(®->wps->dev, probe)) { wpabuf_free(beacon); wpabuf_free(probe); @@ -1592,6 +1641,7 @@ int wps_build_credential_wrap(struct wpabuf *msg, int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) { struct wpabuf *cred; + struct wps_registrar *reg = wps->wps->registrar; if (wps->wps->registrar->skip_cred_build) goto skip_cred_build; @@ -1603,6 +1653,29 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) } os_memset(&wps->cred, 0, sizeof(wps->cred)); + if (wps->peer_dev.multi_ap_ext == MULTI_AP_BACKHAUL_STA && + reg->multi_ap_backhaul_ssid_len) { + wpa_printf(MSG_DEBUG, "WPS: Use backhaul STA credentials"); + os_memcpy(wps->cred.ssid, reg->multi_ap_backhaul_ssid, + reg->multi_ap_backhaul_ssid_len); + wps->cred.ssid_len = reg->multi_ap_backhaul_ssid_len; + /* Backhaul is always WPA2PSK */ + wps->cred.auth_type = WPS_AUTH_WPA2PSK; + wps->cred.encr_type = WPS_ENCR_AES; + /* Set MAC address in the Credential to be the Enrollee's MAC + * address + */ + os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN); + if (reg->multi_ap_backhaul_network_key) { + os_memcpy(wps->cred.key, + reg->multi_ap_backhaul_network_key, + reg->multi_ap_backhaul_network_key_len); + wps->cred.key_len = + reg->multi_ap_backhaul_network_key_len; + } + goto use_provided; + } + os_memcpy(wps->cred.ssid, wps->wps->ssid, wps->wps->ssid_len); wps->cred.ssid_len = wps->wps->ssid_len; @@ -1845,7 +1918,7 @@ static struct wpabuf * wps_build_m2(struct wps_data *wps) wps_build_config_error(msg, WPS_CFG_NO_ERROR) || wps_build_dev_password_id(msg, wps->dev_pw_id) || wps_build_os_version(&wps->wps->dev, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -1913,7 +1986,7 @@ static struct wpabuf * wps_build_m2d(struct wps_data *wps) wps_build_assoc_state(wps, msg) || wps_build_config_error(msg, err) || wps_build_os_version(&wps->wps->dev, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -1949,7 +2022,7 @@ static struct wpabuf * wps_build_m4(struct wps_data *wps) wps_build_r_snonce1(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_clear_free(plain); wpabuf_free(msg); @@ -1984,7 +2057,7 @@ static struct wpabuf * wps_build_m6(struct wps_data *wps) wps_build_r_snonce2(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_clear_free(plain); wpabuf_free(msg); @@ -2021,7 +2094,7 @@ static struct wpabuf * wps_build_m8(struct wps_data *wps) (!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_clear_free(plain); wpabuf_clear_free(msg); @@ -2705,6 +2778,7 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, wps->use_psk_key = 1; } #endif /* WPS_WORKAROUNDS */ + wps_process_vendor_ext_m1(&wps->peer_dev, attr->multi_ap_ext); wps->state = SEND_M2; return WPS_CONTINUE; diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c index 0c458c6adef9d..ca893a43c64b4 100644 --- a/src/wps/wps_upnp.c +++ b/src/wps/wps_upnp.c @@ -599,7 +599,7 @@ static struct wpabuf * build_fake_wsc_ack(void) wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE); wpabuf_put_be16(msg, WPS_NONCE_LEN); wpabuf_put(msg, WPS_NONCE_LEN); - if (wps_build_wfa_ext(msg, 0, NULL, 0)) { + if (wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } diff --git a/src/wps/wps_validate.c b/src/wps/wps_validate.c index 267b565e47847..5c12bce252397 100644 --- a/src/wps/wps_validate.c +++ b/src/wps/wps_validate.c @@ -1057,7 +1057,7 @@ static int wps_validate_cred(const u8 *cred, size_t len) } -static int wps_validate_credential(const u8 *cred[], size_t len[], size_t num, +static int wps_validate_credential(const u8 *cred[], u16 len[], size_t num, int mandatory) { size_t i; |